View Javadoc
1   /*
2    * BrowserTable.java
3    *
4    * Created on den 29 januari 2003, 10:25
5    */
6   
7   package net.sourceforge.jsh3modtool.gui.imagetable;
8   
9   
10  import java.awt.Color;
11  import java.awt.Component;
12  import java.awt.Dimension;
13  import java.awt.Point;
14  import java.awt.event.*;
15  import java.util.*;
16  
17  import javax.swing.table.*;
18  import javax.swing.*;
19  
20  import net.sourceforge.jsh3modtool.gui.imagetable.cache.ImageCache;
21  import net.sourceforge.jsh3modtool.gui.imagetable.cellfilters.SelectedImagesFilter;
22  import net.sourceforge.jsh3modtool.gui.imagetable.utils.JTableUtil;
23   
24  /***
25   * A JTable that displays images and folders.
26   * 
27   * The image cells can have the following states:
28   * <ul>
29   * <li> Selected or not - if an image is selected then it has a blue border around it. 
30   * Selected images can be add to the send list or other operations on them done.
31   * <li> Tagged or not - if an image is tagged, then it has a check in the checkbox. A tagged
32   * image is readonly.
33   * <li> Active - Only one image can be active. The active image is displayed in the preview
34   * pane and is Active for editing.
35   * </ul>
36   *
37   * @author  erma
38   */
39  public class ImageTable extends JTable
40  {
41      /*** The spacing between the cells. */
42      public static final int CELL_SPACING = 2;
43      
44      //private     ImageBrowser parentBrowser;
45  
46      // Cell renderers
47      final private     ImageCellRenderer imageCellRenderer;
48      final private     FolderCellRenderer folderCellRenderer;
49      final private     EmptyCellRenderer emptyCellRenderer;
50  
51  
52      final private     ImageTableModel model;
53      final private     ImageCache imagesCache;
54      
55      private     ImageCell activeCell;
56      /*** The last image that the user pressed CTRL + left mouse on */
57      private     ImageCell lastControlImage;
58  
59      private     int rowHeight;
60      private     int columnWidth;
61  
62  	private JPopupMenu popupMenu;
63  
64  	private int activeColumntCount;
65  
66  	public ImageTable()
67  	{
68  	    this(100,75);
69  	}
70  	
71      /*** 
72       * Creates a new instance of BrowseTable
73       */
74      public ImageTable(int width, int height )
75      {
76      	super();
77          model = new ImageTableModel(1);
78          imagesCache = new ImageCache( model );
79          imageCellRenderer = new ImageCellRenderer( this, imagesCache );
80          folderCellRenderer = new FolderCellRenderer();
81          emptyCellRenderer = new EmptyCellRenderer();
82  
83          rowHeight = height;
84          columnWidth = width;
85          updateColumnRenderers();
86          
87          setModel( model );
88          model.addTableModelListener( this );
89  
90          addMouseListener( new TableMouseListener() );
91  
92          setColumnSelectionAllowed(false);
93          setRowSelectionAllowed(false);
94          setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
95  
96          //setRowHeight( getRowHeight() );
97          setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
98  
99  		this.setIntercellSpacing(new Dimension(CELL_SPACING, CELL_SPACING));
100 
101         super.setShowHorizontalLines( false );
102         super.setShowVerticalLines( false );
103 
104         setBackground( Color.white );
105         setTableHeader(null);
106         
107         activeColumntCount = 0;
108     }
109     
110     /***
111      * Returns this table's model.
112      * This is a convenience method instead of the JTables own <code>getModel()</code>.
113      * @return this table's model.
114      */
115     public ImageTableModel getBrowserTableModel()
116     {
117         return model;
118     }
119     
120     /***
121      * Revalidates the size of a cell.
122      * If the cell size has changed then the whole table is revalidated.
123      */
124     public final void setImageSize(final int width,final int height)
125     {
126     	final Dimension thumbDim = new Dimension(width,height);
127 
128         if ( ( thumbDim.width != columnWidth ) || ( thumbDim.height != rowHeight ) )
129         {
130             imageCellRenderer.resetPanel();
131             imagesCache.clear();
132             rowHeight = thumbDim.height;
133             columnWidth = thumbDim.width;
134             revalidate();
135         }
136 
137     }
138 
139 	/***
140 	 * Returns the selected cell
141 	 * @return selected cell, can be null if no cell is selected
142 	 */
143 	public ImageCell getActiveImage()
144 	{
145 		return activeCell;
146 	}
147     
148     /*** Returns the number of images in the table
149      * @return the number of items in the table
150      */
151     public int getModelSize()
152     {
153         return model.getSize();
154     }
155 
156     /***
157      * Resets the active cell to no active.
158      */
159 	/*private void resetActiveCell()
160 	{
161 		activeCell = null;
162 	}*/
163 
164     /***
165      * Removes all cells from the table
166      */
167     public void clearModel()
168     {
169         model.clear();
170         lastControlImage = null;
171     }
172 
173     /***
174      * Updates the column renderers, sets the correct width and disables the
175      *resizing
176      */
177     private final void updateColumnRenderers()
178     {
179         int colSize = getColumnModel().getColumnCount();
180         for ( int i = 0; i < colSize; i++ )
181         {
182             getColumnModel().getColumn( i ).setMinWidth( getColumnWidth() );
183             getColumnModel().getColumn( i ).setMaxWidth( getColumnWidth() );
184             getColumnModel().getColumn( i ).setResizable( false );
185         }
186     }
187 
188     /***
189      * Returns the width of the columns
190      * @return width of one column
191      */
192     public int getColumnWidth()
193     {
194         return columnWidth;
195     }
196 
197     /***
198      * Returns the height of the row
199      * @return height of one row
200      */
201     public int getRowHeight()
202     {
203         return rowHeight;
204     }
205 
206     /*** 
207      * Updates the model number of columns
208      * @param cols new number of columns
209      */
210     public void setColumnCount( final int cols )
211     {
212         if ( activeColumntCount != cols )
213         {
214         	activeColumntCount = cols;
215             JViewport viewport = (JViewport) getParent();
216             int topRow = Math.round( (float) viewport.getViewPosition().y / (float) getRowHeight() );
217             int imageIndex = topRow * model.getColumnCount();
218 
219             model.setColumnCount( cols );
220             updateColumnRenderers();
221 
222             int newRow = imageIndex / model.getColumnCount();
223 
224             if ( activeCell == null )
225             {
226                 JTableUtil.scrollToVisible( this, newRow, 0 );
227             } 
228             else
229             {
230                 JTableUtil.scrollToCenter( this, model.getTableCell( activeCell ) );
231             }
232         }
233     }
234 
235     /***
236      * Returns the TableCellRenderer for the specified cell.
237      * @param row the cell's row
238      * @param column the cell's column
239      * @return a TableCellRenderer, never null
240      */
241     public TableCellRenderer getCellRenderer(final int row, final int column)
242     {
243         TableCellRenderer cellRenderer = emptyCellRenderer;
244         Object object = model.getValueAt(row, column);
245         if ( object instanceof FolderCell )
246         {
247             cellRenderer = folderCellRenderer;
248         } 
249         else
250         if ( object instanceof ImageCell )
251         {
252             cellRenderer = imageCellRenderer;
253         }
254         return cellRenderer;
255     }
256 
257     /***
258      * Returns the cell that is active for editing.
259      * @return the cell that is active for editing., can be null if no image is selected
260      */
261     public ImageCell getActiveCell()
262     {
263         return activeCell;
264     }
265     
266     /***
267      * Finds the ImageCell matching the specified File and selects it for editing.
268      * @param file the file to be selected for editing.
269      */
270     /*public void setActiveFile(java.io.File file)
271     {
272         selectionWatchCollection.add( file );
273     }*/
274     
275     /***
276      * Adds the following list to the selection watch list.
277      * The list must contains filenames (String). 
278      * @param watchList the filenames to add to the list.
279      */    
280   /*  public void addToSelectionWatchlist( List watchList )
281     {
282         for ( Iterator iterator = watchList.iterator(); iterator.hasNext(); )
283         {
284             File file = new File( (String) iterator.next() );          
285             selectionWatchCollection.add( file );
286         }
287        // parentBrowser.resetActive();
288     }*/
289     
290     /***
291      * Sets the table selected for editing (ACTIVE) cell to the specified.
292      * If the cell is not the same as the old selected then the cell is updated
293      * and the table announces to the ImageBrowser that a new cell has been selected
294      * @param cell the new selected cell
295      */
296     private void setActiveCell( final ImageCell cell )
297     { 
298         if ( ( activeCell == null ) || ( cell != activeCell ) )
299         {
300             ImageCell oldCell = activeCell;
301             activeCell = cell;
302             if ( oldCell != null )
303             {
304                 model.fireTableCellUpdate( oldCell );
305             }
306             selectCell( activeCell, true );
307         }
308     }
309     
310     /***
311      * Toggle the selection of a cell. 
312      * @param cell the cell to select.
313      */
314     private void toggleCellSelection( final ImageCell cell )
315     {
316         selectCell( cell, ! cell.isSelected() );
317     }
318     
319     /***
320      * Selects the cell. A selected cell can be added to the sendlist and has
321      * a blue border around it.
322      * @param cell the cell to select.
323      * @param isSelected if the cell is to be selected or not.
324      */
325     private void selectCell( final ImageCell cell, final boolean isSelected )
326     {
327         if ( cell.isSelected() != isSelected )
328         {
329             cell.setSelected(isSelected);
330             model.fireTableCellUpdate( cell );
331           //  parentBrowser.setSelected( cell.getImageProperties(), isSelected );
332         }
333     }
334     
335     /***
336      * Selects a list of image cells. The user can begin to select
337      * one image and then select another cell further down. This method
338      * selects all cells between the first and the last one.
339      * @param cell the latest pressed image cell.
340      * @param includeOldSelection if the previous selection should still be selected or not.
341      */
342     private void selectCellList(final ImageCell cell, final boolean includeOldSelection)
343     {
344         BaseCell startCell = lastControlImage;
345         BaseCell endCell = cell;
346         
347         int startIndex = 0;
348         if ( startCell != null )
349         {
350             startIndex = model.getImageIndex(startCell);
351         }
352         
353         int endIndex = model.getImageIndex(endCell);
354         
355         if ( startIndex > endIndex )
356         {
357             int temp = endIndex;
358             endIndex = startIndex;
359             startIndex = temp;
360         }
361         
362         if ( ! includeOldSelection )
363         {
364             deselectAllImages();
365         }
366                 
367         for ( int i = startIndex; i <= endIndex; i++ )
368         {
369             BaseCell tempCell = model.getCell(i);
370             if ( ( tempCell != null ) && ( tempCell.getClass().equals(ImageCell.class) ) )
371             {
372                 ((ImageCell)tempCell).setSelected(true);
373                 //parentBrowser.setSelected( ((ImageCell)tempCell).getImageProperties(), true );
374             }
375         }  
376         
377         model.fireTableRowsUpdated( model.getTableCell(startIndex).row, model.getTableCell(endIndex).row );
378     }
379 
380     /***
381      * Fires an update event for the active cell, the active cell is the cell selected for editing.
382      */
383     public void updateActiveCell()
384     {
385         if ( activeCell != null )
386         {
387             model.fireTableCellUpdate( activeCell );
388         }
389     }
390     
391     /***
392      * Tags the cell.
393      * @param cell the image to tag.
394      */
395   /*  private void tagCell(ImageCell cell)
396     {
397         cell.toggleTagged(); 
398         model.fireTableCellUpdate( cell );
399     }*/
400 
401     /***
402      * Selects all images, and repaints the component.
403      * The ImageBrowser is notified of the selection
404      */
405     public void selectAllImages()
406     {
407     	final List images = model.getImageCells( new SelectedImagesFilter(false) );
408         for ( ListIterator iterator = images.listIterator(); iterator.hasNext(); )
409         {
410             ImageCell image = (ImageCell) iterator.next();
411             toggleCellSelection( image );
412         }
413         model.fireTableDataChanged();
414     }
415 
416     /***
417      * Deselects all images, and repaints the component.
418      * The ImageBrowser is notified of the deselection
419      */
420     public void deselectAllImages()
421     {
422     	final List images = model.getImageCells(new SelectedImagesFilter(true));
423         for ( ListIterator iterator = images.listIterator(); iterator.hasNext(); )
424         {
425             ImageCell image = (ImageCell) iterator.next();
426             toggleCellSelection( image );
427         }
428         model.fireTableDataChanged();
429     }
430 
431     /***
432      * Selects tagged images and repaints the component.
433      * The ImageBrowser is notified of the selection.
434      */
435    /* public void selectTaggedImages()
436     {
437         List images = model.getAllImageCells();
438         for ( ListIterator iterator = images.listIterator(); iterator.hasNext(); )
439         {
440             ImageCell image = (ImageCell) iterator.next();
441             selectCell( image, image.isTagged() );
442         }
443         model.fireTableDataChanged();
444     }*/
445 
446     /***
447      * All selected images get the supplied ipr.
448      * @param ipr the new ipr properties.
449      */
450  /*   public void applyIprToAll(IprProperties ipr)
451     {
452         List images = model.getImageCells( new SelectedImagesFilter(true));
453         for ( ListIterator iterator = images.listIterator(); iterator.hasNext(); )
454         {
455             ImageCell image = (ImageCell) iterator.next();
456             image.updateIprProperties(ipr);
457         }
458     }*/
459 
460     /***
461      * Updates the image cache so the selected cell needs to reload the thumbnail.
462      */
463     public void resetCachedImage()
464     {
465         if ( activeCell != null )
466         {
467             imagesCache.reloadCachedImage( activeCell );
468             updateActiveCell();
469         }
470     }
471 
472     /***
473      * The user has pressed on a Folder cell.
474      * @param folder the foldercell
475      * @param event the mouse event
476      */
477     private void leftMouseEventOnFolder( final FolderCell folder, final MouseEvent event )
478     {
479         if ( event.getClickCount() >= 2 )
480         {
481             //parentBrowser.openDirectory( folder.getTargetPath() );
482         }
483     }
484 
485     /***
486      * The user has pressed on an Image cell
487      * @param cell the table cell
488      * @param image the image cell
489      * @param event the mouse event
490      */
491     private void leftMouseEventOnImage( final JTableUtil.Cell cell, final ImageCell image, final MouseEvent event )
492     {
493         //relative Point in cell
494     	final Point relativePoint = new Point();
495         relativePoint.x = event.getX() - cell.column * getColumnWidth();
496         relativePoint.y = event.getY() - cell.row * getRowHeight();
497 
498         // is in checkbox
499        // Rectangle checkBox = imageCellRenderer.getCheckbox();
500 //        Rectangle bottomBox = new Rectangle( 0, checkBox.y, getColumnWidth(), checkBox.height );
501 
502       /*  if ( bottomBox.contains( relativePoint ) )
503         {
504          //   tagCell(image);        
505         } 
506         else*/
507         {
508             if ( event.isShiftDown() && event.isMetaDown() )
509             {
510                 selectCellList( image, true );
511             } 
512             else
513             if ( event.isShiftDown() )
514             {
515                 selectCellList( image, false );
516             } 
517             else
518             if ( //( OSUtil.isMacintosh() && event.isMetaDown()) || 
519                  ( event.isControlDown() ) )
520             {
521                 lastControlImage = image;
522                 toggleCellSelection( image );
523             } 
524             else
525             {
526                 deselectAllImages();
527                 lastControlImage = image;
528                 setActiveCell( image );
529             }
530         }
531         model.fireTableCellUpdated( cell.row, cell.column );
532     }
533 
534     /***
535      * The user has pressed in the table, determines if the cell under the mouse is
536      * a folder or an image and dispatches the event to the correct handler,
537      * @see mouseClickedOnFolder
538      * @see mouseClickedOnImage
539      * @param event the mouse event
540      */
541     protected void leftMouseButtonClickedInTable( final MouseEvent event )
542     {
543     	final JTableUtil.Cell cell = JTableUtil.getCell( this, event.getX(), event.getY() );
544         if ( cell != null )
545         {
546         	final Object value = model.getValueAt( cell.row, cell.column );
547             if ( value == null )
548             {
549                 return;
550             }
551 
552             if ( value.getClass().equals( FolderCell.class ) )
553             {
554                 leftMouseEventOnFolder( (FolderCell) value, event );
555             } 
556             else
557             {
558                 leftMouseEventOnImage( cell, (ImageCell) value, event );
559             }
560         }
561     }
562 
563 	/***
564 	 * The user has pressed in the table, determines if the cell under the mouse is
565 	 * a folder or an image and dispatches the event to the correct handler,
566 	 * @see mouseClickedOnFolder
567 	 * @see mouseClickedOnImage
568 	 * @param event the mouse event
569 	 */
570 	protected void rightMouseButtonClickedInTable( final MouseEvent event )
571 	{
572 		final JTableUtil.Cell cell = JTableUtil.getCell( this, event.getX(), event.getY() );
573 		if ( cell != null )
574 		{
575 			final Object value = model.getValueAt( cell.row, cell.column );
576 			if ( value == null )
577 			{
578 				return;
579 			}
580 
581 			if ( ( value.getClass().equals( ImageCell.class ) ) && 
582                  ( event.isPopupTrigger() ) )
583 			{
584 				final ImageCell imageCell = (ImageCell) value;
585                 //if ( ! model.getSelectedImageProperties().contains( imageCell.getImageProperties() ))
586                 {
587                     deselectAllImages();
588                     setActiveCell( imageCell );
589                 } 
590 				popupMenu.show(event.getComponent(), event.getX(), event.getY());
591 			}
592 		}
593 	}
594 
595     /***
596      * Mouse listener class
597      */
598     private class TableMouseListener extends MouseAdapter
599     {
600         /*** If the left mouse button is clicked then it selects the image/folder 
601          * {@inheritDoc}
602          */
603         public void mouseClicked(final MouseEvent event)
604         {
605             //if ( e.getButton() == MouseEvent.BUTTON1 )
606             if ( ( event.getModifiers() & InputEvent.BUTTON1_MASK) != 0)
607             {
608                 leftMouseButtonClickedInTable( event );
609             }
610             
611         }
612         
613 		// Need to test both for pressed and released events since it
614 		// can be different for different platforms.
615         /*** {@inheritDoc} */
616 		public void mousePressed(final MouseEvent e)
617 		{
618 			if ( e.isPopupTrigger() )
619 			{
620 				rightMouseButtonClickedInTable( e );
621 			}
622 		}
623 
624         /*** {@inheritDoc} */
625 		public void mouseReleased(final MouseEvent e)
626 		{
627 			if ( e.isPopupTrigger() )
628 			{
629 				rightMouseButtonClickedInTable( e );   	
630 			}
631 		}
632     }
633 
634     /***
635      * Dummy TableCellRenderer for empty cells, used for performace
636      */
637     private class EmptyCellRenderer implements TableCellRenderer
638     {
639         /*** {@inheritDoc} */
640         public Component getTableCellRendererComponent(final JTable table, final Object value, 
641         		final boolean isSelected, final boolean hasFocus, final int row, final int column)
642         {
643             return null;
644         }
645     }
646 }