1
2
3
4
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
45
46
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
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
160
161
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
271
272
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
281
282
283
284
285
286
287
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
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
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
396
397
398
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
436
437
438
439
440
441
442
443
444
445
446 /***
447 * All selected images get the supplied ipr.
448 * @param ipr the new ipr properties.
449 */
450
451
452
453
454
455
456
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
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
494 final Point relativePoint = new Point();
495 relativePoint.x = event.getX() - cell.column * getColumnWidth();
496 relativePoint.y = event.getY() - cell.row * getRowHeight();
497
498
499
500
501
502
503
504
505
506
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 (
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
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
606 if ( ( event.getModifiers() & InputEvent.BUTTON1_MASK) != 0)
607 {
608 leftMouseButtonClickedInTable( event );
609 }
610
611 }
612
613
614
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 }