1
2
3
4
5
6
7
8
9
10
11 package de.jaret.util.ui.table;
12
13 import java.beans.PropertyChangeEvent;
14 import java.beans.PropertyChangeListener;
15 import java.beans.PropertyChangeSupport;
16 import java.lang.reflect.Constructor;
17 import java.util.ArrayList;
18 import java.util.Collection;
19 import java.util.Collections;
20 import java.util.Comparator;
21 import java.util.Date;
22 import java.util.HashMap;
23 import java.util.HashSet;
24 import java.util.List;
25 import java.util.Map;
26 import java.util.Set;
27 import java.util.Vector;
28
29 import org.eclipse.swt.SWT;
30 import org.eclipse.swt.events.DisposeEvent;
31 import org.eclipse.swt.events.DisposeListener;
32 import org.eclipse.swt.events.KeyEvent;
33 import org.eclipse.swt.events.KeyListener;
34 import org.eclipse.swt.events.MouseEvent;
35 import org.eclipse.swt.events.MouseListener;
36 import org.eclipse.swt.events.MouseMoveListener;
37 import org.eclipse.swt.events.MouseTrackListener;
38 import org.eclipse.swt.events.PaintEvent;
39 import org.eclipse.swt.events.PaintListener;
40 import org.eclipse.swt.events.SelectionAdapter;
41 import org.eclipse.swt.events.SelectionEvent;
42 import org.eclipse.swt.graphics.Color;
43 import org.eclipse.swt.graphics.GC;
44 import org.eclipse.swt.graphics.Image;
45 import org.eclipse.swt.graphics.Point;
46 import org.eclipse.swt.graphics.Rectangle;
47 import org.eclipse.swt.widgets.Canvas;
48 import org.eclipse.swt.widgets.Composite;
49 import org.eclipse.swt.widgets.Control;
50 import org.eclipse.swt.widgets.Display;
51 import org.eclipse.swt.widgets.Event;
52 import org.eclipse.swt.widgets.Listener;
53 import org.eclipse.swt.widgets.Menu;
54 import org.eclipse.swt.widgets.ScrollBar;
55 import org.eclipse.swt.widgets.Shell;
56
57 import de.jaret.util.date.JaretDate;
58 import de.jaret.util.misc.PropertyObservable;
59 import de.jaret.util.misc.PropertyObservableBase;
60 import de.jaret.util.ui.table.editor.BooleanCellEditor;
61 import de.jaret.util.ui.table.editor.DateCellEditor;
62 import de.jaret.util.ui.table.editor.DoubleCellEditor;
63 import de.jaret.util.ui.table.editor.EnumComboEditor;
64 import de.jaret.util.ui.table.editor.ICellEditor;
65 import de.jaret.util.ui.table.editor.IntegerCellEditor;
66 import de.jaret.util.ui.table.editor.TextCellEditor;
67 import de.jaret.util.ui.table.filter.DefaultAutoFilter;
68 import de.jaret.util.ui.table.filter.IAutoFilter;
69 import de.jaret.util.ui.table.filter.IRowFilter;
70 import de.jaret.util.ui.table.model.DefaultHierarchicalTableViewState;
71 import de.jaret.util.ui.table.model.DefaultTableViewState;
72 import de.jaret.util.ui.table.model.IColumn;
73 import de.jaret.util.ui.table.model.IHierarchicalJaretTableModel;
74 import de.jaret.util.ui.table.model.IHierarchicalTableViewState;
75 import de.jaret.util.ui.table.model.IJaretTableCell;
76 import de.jaret.util.ui.table.model.IJaretTableModel;
77 import de.jaret.util.ui.table.model.IJaretTableModelListener;
78 import de.jaret.util.ui.table.model.IJaretTableSelection;
79 import de.jaret.util.ui.table.model.IJaretTableSelectionModel;
80 import de.jaret.util.ui.table.model.IJaretTableSelectionModelListener;
81 import de.jaret.util.ui.table.model.IRow;
82 import de.jaret.util.ui.table.model.IRowSorter;
83 import de.jaret.util.ui.table.model.ITableFocusListener;
84 import de.jaret.util.ui.table.model.ITableNode;
85 import de.jaret.util.ui.table.model.ITableViewState;
86 import de.jaret.util.ui.table.model.ITableViewStateListener;
87 import de.jaret.util.ui.table.model.JaretTableCellImpl;
88 import de.jaret.util.ui.table.model.JaretTableSelectionModelImpl;
89 import de.jaret.util.ui.table.model.StdHierarchicalTableModel;
90 import de.jaret.util.ui.table.model.ITableViewState.RowHeightMode;
91 import de.jaret.util.ui.table.renderer.BooleanCellRenderer;
92 import de.jaret.util.ui.table.renderer.DateCellRenderer;
93 import de.jaret.util.ui.table.renderer.DefaultTableHeaderRenderer;
94 import de.jaret.util.ui.table.renderer.DoubleCellRenderer;
95 import de.jaret.util.ui.table.renderer.ICellRenderer;
96 import de.jaret.util.ui.table.renderer.ICellStyle;
97 import de.jaret.util.ui.table.renderer.IHierarchyRenderer;
98 import de.jaret.util.ui.table.renderer.ITableHeaderRenderer;
99 import de.jaret.util.ui.table.renderer.ImageCellRenderer;
100 import de.jaret.util.ui.table.renderer.TableHierarchyRenderer;
101 import de.jaret.util.ui.table.renderer.TextCellRenderer;
102 import de.jaret.util.ui.table.strategies.DefaultCCPStrategy;
103 import de.jaret.util.ui.table.strategies.DefaultFillDragStrategy;
104 import de.jaret.util.ui.table.strategies.ICCPStrategy;
105 import de.jaret.util.ui.table.strategies.IFillDragStrategy;
106
107 /***
108 * Custom drawn table widget for the SWT Toolkit. Always consider using the native table widget!
109 * <p>
110 * The JaretTable features:
111 * </p>
112 * <ul>
113 * <li>Flexible rendering of all elements</li>
114 * <li>CellEditor support</li>
115 * <li>Flat (table) and hierarchical (tree) support</li>
116 * <li>Separation between data model and viewstate</li>
117 * <li>row filtering and sorting without model modification</li>
118 * <li>simple auto filter</li>
119 * <li>drag fill support </li>
120 * <li></li>
121 * <li></li>
122 * </ul>
123 *
124 * Keyboard controls <table>
125 * <tr>
126 * <td><b>Key</b></td>
127 * <td><b>Function</b></td>
128 * </tr>
129 * <tr>
130 * <td>Shift+Click</td>
131 * <td>Move focus</td>
132 * </tr>
133 * <tr>
134 * <td>Arrows</td>
135 * <td>Move focus</td>
136 * </tr>
137 * <tr>
138 * <td>Shift-Arrow</td>
139 * <td>Select Current cell, shift focus and select newly focussed cell and the rectangle of cells between first and
140 * current cell</td>
141 * </tr>
142 * </table>
143 *
144 * @author Peter Kliem
145 * @version $Id: JaretTable.java 1076 2010-12-05 13:34:42Z kliem $
146 */
147 public class JaretTable extends Canvas implements ITableViewStateListener, IJaretTableModelListener,
148 IJaretTableSelectionModelListener, PropertyChangeListener, PropertyObservable {
149 /*** true for measuring paint time. */
150 private static final boolean DEBUGPAINTTIME = false;
151
152 /*** pixel for row resize/selection if no fixed rows are present. */
153 private static final int SELDELTA = 4;
154
155 /*** size of the marker for fill dragging. */
156 private static final int FILLDRAGMARKSIZE = 4;
157
158 /*** default height for the header. */
159 private static final int DEFAULTHEADERHEIGHT = 16;
160
161 /*** default value for the minimal header height. */
162 private static final int DEFAULTMINHEADERHEIGHT = 10;
163
164 /*** button that is the popuptrigger. */
165 private static final int POPUPTRIGGER = 3;
166
167 /*** name of the bound property. */
168 public static final String PROPERTYNAME_HEADERHEIGHT = "HeaderHeight";
169 /*** name of the bound property. */
170 public static final String PROPERTYNAME_FIRSTROWIDX = "FirstRowIdx";
171 /*** name of the bound property. */
172 public static final String PROPERTYNAME_FIRSTROWPIXELOFFSET = "FirstRowPixelOffset";
173 /*** name of the bound property. */
174 public static final String PROPERTYNAME_ROWSORTER = "RowSorter";
175 /*** name of the bound property. */
176 public static final String PROPERTYNAME_ROWFILTER = "RowFilter";
177 /*** Pseudo propertyname on which property change is fired whenever the sorting changes. */
178 public static final String PROPERTYNAME_SORTING = "Sorting";
179 /*** Pseudo propertyname on which property change is fired whenever the filtering changes. */
180 public static final String PROPERTYNAME_FILTERING = "Filtering";
181 /*** name of the bound property. */
182 public static final String PROPERTYNAME_AUTOFILTERENABLE = "AutoFilterEnable";
183
184
185 /*** Index of the first row displayed (may be only a half display). */
186 protected int _firstRowIdx = 0;
187 /*** Pixel offset of the display of the first row. */
188 protected int _firstRowPixelOffset = 0;
189 /*** Index of the first row displayed. */
190 protected int _firstColIdx = 0;
191 /*** pixel offset of the firs displayed column. */
192 protected int _firstColPixelOffset = 0;
193
194 /*** number of fixed columns. */
195 protected int _fixedColumns = 0;
196 /*** number of fixed rows. */
197 protected int _fixedRows = 0;
198
199 /*** cell renderer map for columns. */
200 protected Map<IColumn, ICellRenderer> _colCellRendererMap = new HashMap<IColumn, ICellRenderer>();
201
202 /*** cell renderer map for classes. */
203 protected Map<Class<?>, ICellRenderer> _colClassRendererMap = new HashMap<Class<?>, ICellRenderer>();
204
205 /*** cell editor map for columns. */
206 protected Map<IColumn, ICellEditor> _colCellEditorMap = new HashMap<IColumn, ICellEditor>();
207
208 /*** cell editor map for classes. */
209 protected Map<Class<?>, ICellEditor> _colClassEditorMap = new HashMap<Class<?>, ICellEditor>();
210
211 /*** configuration: support fill dragging. */
212 protected boolean _supportFillDragging = true;
213
214 /*** fill drag strategy. * */
215 protected IFillDragStrategy _fillDragStrategy = new DefaultFillDragStrategy();
216
217 /*** Strategy for handling cut copy paste. */
218 protected ICCPStrategy _ccpStrategy = new DefaultCCPStrategy();
219
220 /*** table model. */
221 protected IJaretTableModel _model;
222
223 /*** hierarchical table model if used. */
224 protected IHierarchicalJaretTableModel _hierarchicalModel;
225
226 /*** table viewstate. */
227 protected ITableViewState _tvs = new DefaultTableViewState();
228
229 /*** List of rows actually diplayed (filtered and ordered). */
230 protected List<IRow> _rows = new ArrayList<IRow>();
231
232 /*** row filter. */
233 protected IRowFilter _rowFilter;
234
235 /*** row sorter. */
236 protected IRowSorter _rowSorter;
237
238 /*** List of columns actually displayed. */
239 protected List<IColumn> _cols = new ArrayList<IColumn>();
240
241 /*** Rectangle the headers are painted in. */
242 protected Rectangle _headerRect;
243
244 /*** height of the headers. */
245 protected int _headerHeight = DEFAULTHEADERHEIGHT;
246
247 /*** minimal height for the header. */
248 protected int _minHeaderHeight = DEFAULTMINHEADERHEIGHT;
249
250 /*** if true headers will be drawn. */
251 protected boolean _drawHeader = true;
252
253 /*** rectangle the main table is drawn into (withou fixedcolRect and without fixedRowRect!). */
254 protected Rectangle _tableRect;
255
256 /*** Renderer used to render the headers. */
257 protected ITableHeaderRenderer _headerRenderer = new DefaultTableHeaderRenderer();
258
259 /*** Rectangle in which the fixed columns will be painted. */
260 protected Rectangle _fixedColRect;
261
262 /*** Rectangle in which the fixed rows will be painted. */
263 protected Rectangle _fixedRowRect;
264
265 /*** cache for the drag marker location. */
266 protected Rectangle _dragMarkerRect;
267
268 /*** Rectangle the autofilter elements (combos) are placed. */
269 protected Rectangle _autoFilterRect;
270
271 /*** if true autofilters are enabled and present. */
272 protected boolean _autoFilterEnabled = false;
273
274 /*** Instance of the interbal RowFilter that makes up the autofilter. */
275 protected AutoFilter _autoFilter = new AutoFilter(this);
276
277 /*** map containing the actual instantiated autofilters for the different columns. */
278 protected Map<IColumn, IAutoFilter> _autoFilterMap = new HashMap<IColumn, IAutoFilter>();
279
280 /*** map containing the autofilter classes to use for dedicated content classes. */
281 protected Map<Class<?>, Class<? extends IAutoFilter>> _autoFilterClassMap = new HashMap<Class<?>, Class<? extends IAutoFilter>>();
282
283 /*** map containing teh autofiletr classes to use for specified columns. */
284 protected Map<IColumn, Class<? extends IAutoFilter>> _autoFilterColumnMap = new HashMap<IColumn, Class<? extends IAutoFilter>>();
285
286 /*** if true header resizing is allowed. */
287 private boolean _headerResizeAllowed = true;
288
289 /*** if true row resizes are allowed. */
290 private boolean _rowResizeAllowed = true;
291
292 /*** if true column resizes are allowed. */
293 private boolean _columnResizeAllowed = true;
294
295 /***
296 * If true, resizing is only allowed in header and fixed columns (for rows) and the leftmost SELDELTA pixels of
297 * eachrow.
298 */
299 protected boolean _resizeRestriction = false;
300
301 /*** if true fixed rows will not be affected by sorting operations. */
302 protected boolean _excludeFixedRowsFromSorting = true;
303
304 /*** global flag for allowing sorting of the table. */
305 protected boolean _allowSorting = true;
306
307
308 /*** row of the focussed cell or null. */
309 protected IRow _focussedRow = null;
310
311 /*** column of the focussed cell or null. */
312 protected IColumn _focussedColumn = null;
313
314 /*** Listz of listeners interested in changes of the focussed cell. */
315 protected List<ITableFocusListener> _tableFocusListeners;
316
317 /*** selection model used by the table. */
318 protected IJaretTableSelectionModel _selectionModel = new JaretTableSelectionModelImpl();
319
320
321 /*** cell editor used to edit a cell. will be nun null when editiing. */
322 protected ICellEditor _editor;
323
324 /*** control of the editor. */
325 protected Control _editorControl = null;
326
327 /*** row that is edited. */
328 protected IRow _editorRow;
329
330 /*** context menu used on table headers. */
331 protected Menu _headerContextMenu;
332
333 /*** context menu used for rows. */
334 protected Menu _rowContextMenu;
335
336 /*** Delegate to handle property change listener support. */
337 protected PropertyChangeSupport _propertyChangeSupport;
338
339 /*** row information cache. */
340 protected List<RowInfo> _rowInfoCache = null;
341 /*** column information cache. */
342 protected List<ColInfo> _colInfoCache = new ArrayList<ColInfo>();
343
344 /***
345 * Simple struct for storing row information.
346 *
347 * @author Peter Kliem
348 * @version $Id: JaretTable.java 1076 2010-12-05 13:34:42Z kliem $
349 */
350 public class RowInfo {
351 /*** beginning y coordinate. */
352 public int y;
353 /*** row reference. */
354 public IRow row;
355 /*** height of the row. */
356 public int height;
357 /*** fixed row flag. */
358 public boolean fixed = false;
359
360 /***
361 * Construct a row info instance.
362 *
363 * @param rowIn row reference
364 * @param yIn begin y
365 * @param heightIn height of the row
366 * @param fixedIn true if the row is a fixed row
367 */
368 public RowInfo(IRow rowIn, int yIn, int heightIn, boolean fixedIn) {
369 this.row = rowIn;
370 this.y = yIn;
371 this.height = heightIn;
372 this.fixed = fixedIn;
373 }
374 }
375
376 /***
377 * Simple struct for storing column information.
378 *
379 * @author Peter Kliem
380 * @version $Id: JaretTable.java 1076 2010-12-05 13:34:42Z kliem $
381 */
382 public class ColInfo {
383 /*** begin x coordinate. */
384 public int x;
385
386 /*** column reference. */
387 public IColumn column;
388
389 /*** actual width of the column. */
390 public int width;
391
392 /***
393 * Construct a col info instance.
394 *
395 * @param columnIn col ref
396 * @param xIn begin x
397 * @param widthIn width in pixel
398 */
399 public ColInfo(IColumn columnIn, int xIn, int widthIn) {
400 this.column = columnIn;
401 this.x = xIn;
402 this.width = widthIn;
403 }
404 }
405
406 /***
407 * Constructor for a new JaretTable widget.
408 *
409 * @param parent parent composite
410 * @param style style bits (use HSCROLL, VSCROLL)
411 */
412 public JaretTable(Composite parent, int style) {
413
414 super(parent, style | SWT.NO_REDRAW_RESIZE | SWT.NO_BACKGROUND | SWT.DOUBLE_BUFFERED);
415
416 addPaintListener(new PaintListener() {
417 public void paintControl(PaintEvent event) {
418 onPaint(event);
419 }
420 });
421
422 addMouseListener(new MouseListener() {
423
424 public void mouseDoubleClick(MouseEvent me) {
425 mouseDouble(me.x, me.y);
426 }
427
428 public void mouseDown(MouseEvent me) {
429 forceFocus();
430 mousePressed(me.x, me.y, me.button == POPUPTRIGGER, me.stateMask);
431 }
432
433 public void mouseUp(MouseEvent me) {
434 mouseReleased(me.x, me.y, me.button == POPUPTRIGGER);
435 }
436 });
437
438 addMouseMoveListener(new MouseMoveListener() {
439 public void mouseMove(MouseEvent me) {
440 if ((me.stateMask & SWT.BUTTON1) != 0) {
441 mouseDragged(me.x, me.y, me.stateMask);
442 } else {
443 mouseMoved(me.x, me.y, me.stateMask);
444 }
445 }
446 });
447
448 addMouseTrackListener(new MouseTrackListener() {
449 public void mouseEnter(MouseEvent arg0) {
450 }
451
452 public void mouseExit(MouseEvent arg0) {
453
454 if (Display.getCurrent().getActiveShell() != null) {
455 Display.getCurrent().getActiveShell().setCursor(
456 Display.getCurrent().getSystemCursor(SWT.CURSOR_ARROW));
457 }
458 }
459
460 public void mouseHover(MouseEvent me) {
461 setToolTipText(getToolTipText(me.x, me.y));
462 }
463 });
464
465 addDisposeListener(new DisposeListener() {
466 public void widgetDisposed(DisposeEvent event) {
467 onDispose(event);
468 }
469 });
470
471
472 addKeyListener(new KeyListener() {
473 public void keyPressed(KeyEvent event) {
474 handleKeyPressed(event);
475 }
476
477 public void keyReleased(KeyEvent arg0) {
478 }
479 });
480
481 Listener listener = new Listener() {
482 public void handleEvent(Event event) {
483 switch (event.type) {
484 case SWT.Resize:
485 updateScrollBars();
486 break;
487 default:
488
489 break;
490 }
491 }
492 };
493 addListener(SWT.Resize, listener);
494
495 ScrollBar verticalBar = getVerticalBar();
496 if (verticalBar != null) {
497 verticalBar.addSelectionListener(new SelectionAdapter() {
498 public void widgetSelected(SelectionEvent event) {
499 handleVerticalScroll(event);
500 }
501 });
502 }
503
504 ScrollBar horizontalBar = getHorizontalBar();
505 if (horizontalBar != null) {
506 horizontalBar.addSelectionListener(new SelectionAdapter() {
507 public void widgetSelected(SelectionEvent event) {
508 handleHorizontalScroll(event);
509 }
510 });
511 }
512
513 _tableRect = getClientArea();
514 _fixedColRect = getClientArea();
515
516
517
518
519 registerDefaultRenderers();
520 registerDefaultEditors();
521 registerDefaultAutofilters();
522
523
524 _tvs.addTableViewStateListener(this);
525
526
527 _selectionModel.addTableSelectionModelListener(this);
528
529 setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE));
530
531 _propertyChangeSupport = new PropertyChangeSupport(this);
532 }
533
534 /***
535 * Dispose whatever there is to dispose (renderers and so on).
536 *
537 * @param event dispose event
538 */
539 private void onDispose(DisposeEvent event) {
540
541 if (_headerRenderer != null) {
542 _headerRenderer.dispose();
543 }
544
545 for (ICellRenderer renderer : _colCellRendererMap.values()) {
546 renderer.dispose();
547 }
548 for (ICellRenderer renderer : _colClassRendererMap.values()) {
549 renderer.dispose();
550 }
551
552 for (ICellEditor editor : _colCellEditorMap.values()) {
553 editor.dispose();
554 }
555 for (ICellEditor editor : _colClassEditorMap.values()) {
556 editor.dispose();
557 }
558
559 for (IAutoFilter autoFilter : _autoFilterMap.values()) {
560 autoFilter.dispose();
561 }
562 if (_rowSorter != null) {
563 _rowSorter.removePropertyChangeListener(this);
564 }
565 if (_rowFilter != null) {
566 _rowFilter.removePropertyChangeListener(this);
567 }
568 if (_ccpStrategy != null) {
569 _ccpStrategy.dispose();
570 }
571 }
572
573 /***
574 * Register all default renderers.
575 *
576 */
577 private void registerDefaultRenderers() {
578 ICellRenderer cellRenderer = new TextCellRenderer();
579 registerCellRenderer(void.class, cellRenderer);
580 registerCellRenderer(String.class, cellRenderer);
581 registerCellRenderer(Image.class, new ImageCellRenderer());
582 cellRenderer = new BooleanCellRenderer();
583 registerCellRenderer(Boolean.class, cellRenderer);
584 registerCellRenderer(Boolean.TYPE, cellRenderer);
585 cellRenderer = new DateCellRenderer();
586 registerCellRenderer(Date.class, cellRenderer);
587 registerCellRenderer(JaretDate.class, cellRenderer);
588 cellRenderer = new DoubleCellRenderer();
589 registerCellRenderer(Double.class, cellRenderer);
590 registerCellRenderer(Double.TYPE, cellRenderer);
591 }
592
593 /***
594 * Register all default editors.
595 *
596 */
597 private void registerDefaultEditors() {
598 registerCellEditor(String.class, new TextCellEditor(true));
599 registerCellEditor(Boolean.class, new BooleanCellEditor(true));
600 registerCellEditor(Date.class, new DateCellEditor());
601 registerCellEditor(JaretDate.class, new DateCellEditor());
602 registerCellEditor(Enum.class, new EnumComboEditor());
603 registerCellEditor(Integer.class, new IntegerCellEditor());
604 registerCellEditor(Integer.TYPE, new IntegerCellEditor());
605 registerCellEditor(Double.class, new DoubleCellEditor());
606 registerCellEditor(Double.TYPE, new DoubleCellEditor());
607 }
608
609 /***
610 * Regsiter the default autofilters.
611 */
612 private void registerDefaultAutofilters() {
613 registerAutoFilterForClass(String.class, DefaultAutoFilter.class);
614 }
615
616 /***
617 * Register a cell renderer for rendering objects of class clazz.
618 *
619 * @param clazz class the renderer should be applied for
620 * @param cellRenderer renderer to use for clazz
621 */
622 public void registerCellRenderer(Class<?> clazz, ICellRenderer cellRenderer) {
623 _colClassRendererMap.put(clazz, cellRenderer);
624 }
625
626 /***
627 * Register a cell renderer for a column.
628 *
629 * @param column column the renderer should be used on
630 * @param cellRenderer renderer to use
631 */
632 public void registerCellRenderer(IColumn column, ICellRenderer cellRenderer) {
633 _colCellRendererMap.put(column, cellRenderer);
634 }
635
636 /***
637 * Retrieve the cell renderer for a cell.
638 *
639 * @param row row row of the cell
640 * @param column column column of the cell
641 * @return cell renderer
642 */
643 protected ICellRenderer getCellRenderer(IRow row, IColumn column) {
644
645 ICellRenderer renderer = null;
646 renderer = _colCellRendererMap.get(column);
647 if (renderer == null) {
648
649 Object value = column.getValue(row);
650 if (value != null) {
651 renderer = getCellRendererFromMap(value.getClass());
652 }
653 if (renderer == null) {
654
655 renderer = _colClassRendererMap.get(void.class);
656 }
657 }
658 return renderer;
659 }
660
661 /***
662 * Register a cell editor for objects of class clazz.
663 *
664 * @param clazz class of objeects the editor should be used for
665 * @param cellEditor editor to use
666 */
667 public void registerCellEditor(Class<?> clazz, ICellEditor cellEditor) {
668 _colClassEditorMap.put(clazz, cellEditor);
669 }
670
671 /***
672 * Register a cell editor for a column.
673 *
674 * @param column column the editor should be used for
675 * @param cellEditor editor to use
676 */
677 public void registerCellEditor(IColumn column, ICellEditor cellEditor) {
678 _colCellEditorMap.put(column, cellEditor);
679 }
680
681 /***
682 * Retrieve the cell editor for a cell.
683 *
684 * @param row row of the cell
685 * @param column col of the cell
686 * @return cell editor or <code>null</code> if no editor can be found
687 */
688 private ICellEditor getCellEditor(IRow row, IColumn column) {
689
690 ICellEditor editor = null;
691 editor = _colCellEditorMap.get(column);
692 if (editor == null) {
693
694 Object value = column.getValue(row);
695 if (value != null) {
696 editor = getCellEditorFromMap(value.getClass());
697 } else if (column.getContentClass(row) != null) {
698 editor = getCellEditorFromMap(column.getContentClass(row));
699 } else if (column.getContentClass() != null) {
700 editor = getCellEditorFromMap(column.getContentClass());
701 }
702 }
703 return editor;
704 }
705
706 /***
707 * Retrieve a cell editor for a given class. Checks all interfaces and all superclasses.
708 *
709 * @param clazz class in queston
710 * @return editor or null
711 */
712 private ICellEditor getCellEditorFromMap(Class<?> clazz) {
713 ICellEditor result = null;
714 result = _colClassEditorMap.get(clazz);
715 if (result != null) {
716 return result;
717 }
718
719 Class<?>[] interfaces = clazz.getInterfaces();
720 for (Class<?> c : interfaces) {
721 result = _colClassEditorMap.get(c);
722 if (result != null) {
723 return result;
724 }
725 }
726
727 Class<?> sc = clazz.getSuperclass();
728
729 while (sc != null) {
730 result = _colClassEditorMap.get(sc);
731 if (result != null) {
732 return result;
733 }
734
735 Class<?>[] scinterfaces = sc.getInterfaces();
736 for (Class<?> c : scinterfaces) {
737 result = _colClassEditorMap.get(c);
738 if (result != null) {
739 return result;
740 }
741 }
742 sc = sc.getSuperclass();
743 }
744
745 return result;
746 }
747
748 /***
749 * Retrieve a cell renderer for a given class. Checks all interfaces and all superclasses.
750 *
751 * @param clazz class in queston
752 * @return renderer or null
753 */
754 private ICellRenderer getCellRendererFromMap(Class<?> clazz) {
755 ICellRenderer result = null;
756 result = _colClassRendererMap.get(clazz);
757 if (result != null) {
758 return result;
759 }
760
761 Class<?>[] interfaces = clazz.getInterfaces();
762 for (Class<?> c : interfaces) {
763 result = _colClassRendererMap.get(c);
764 if (result != null) {
765 return result;
766 }
767 }
768
769 Class<?> sc = clazz.getSuperclass();
770
771 while (sc != null) {
772 result = _colClassRendererMap.get(sc);
773 if (result != null) {
774 return result;
775 }
776
777 Class<?>[] scinterfaces = sc.getInterfaces();
778 for (Class<?> c : scinterfaces) {
779 result = _colClassRendererMap.get(c);
780 if (result != null) {
781 return result;
782 }
783 }
784 sc = sc.getSuperclass();
785 }
786
787 return result;
788 }
789
790 /***
791 * Register an autofilter implementing class to be used on columns that announce a specific content class.
792 *
793 * @param clazz content clazz thet triggers the use of the filter
794 * @param autoFilterClass class implementing the IAutoFilter interface that will be used
795 */
796 public void registerAutoFilterForClass(Class<?> clazz, Class<? extends IAutoFilter> autoFilterClass) {
797 _autoFilterClassMap.put(clazz, autoFilterClass);
798 }
799
800 /***
801 * Regsiter an autofilter implementing class for use with a specific column.
802 *
803 * @param column column
804 * @param autoFilterClass class of autofilter that will be used
805 */
806 public void registerAutoFilterForColumn(IColumn column, Class<? extends IAutoFilter> autoFilterClass) {
807 _autoFilterColumnMap.put(column, autoFilterClass);
808 }
809
810 /***
811 * Get the autofiletr class to be used on a column.
812 *
813 * @param column column
814 * @return class or <code>null</code> if none could be determined
815 */
816 protected Class<? extends IAutoFilter> getAutoFilterClass(IColumn column) {
817 Class<? extends IAutoFilter> result = _autoFilterColumnMap.get(column);
818 if (result != null) {
819 return result;
820 }
821 Class<?> contentClass = column.getContentClass();
822 if (contentClass != null) {
823 result = getAutoFilterClassForClass(contentClass);
824 }
825
826 if (result == null) {
827 result = _autoFilterClassMap.get(String.class);
828 }
829 return result;
830 }
831
832 /***
833 * Retrieve autofilter class for a given content class. Takes interfaces and superclasses into account.
834 *
835 * @param clazz content class
836 * @return class to be used or <code>null</code> if none could be determined
837 */
838 private Class<? extends IAutoFilter> getAutoFilterClassForClass(Class<?> clazz) {
839 Class<? extends IAutoFilter> result = null;
840 result = _autoFilterClassMap.get(clazz);
841 if (result != null) {
842 return result;
843 }
844
845 Class<?>[] interfaces = clazz.getInterfaces();
846 for (Class<?> c : interfaces) {
847 result = _autoFilterClassMap.get(c);
848 if (result != null) {
849 return result;
850 }
851 }
852
853 Class<?> sc = clazz.getSuperclass();
854
855 while (sc != null) {
856 result = _autoFilterClassMap.get(sc);
857 if (result != null) {
858 return result;
859 }
860
861 Class<?>[] scinterfaces = sc.getInterfaces();
862 for (Class<?> c : scinterfaces) {
863 result = _autoFilterClassMap.get(c);
864 if (result != null) {
865 return result;
866 }
867 }
868 sc = sc.getSuperclass();
869 }
870
871 return result;
872 }
873
874
875 /*** currently dragged row (dragging height). */
876 protected RowInfo _heightDraggedRowInfo = null;
877
878 /*** currently dragged (width) column or null. */
879 protected ColInfo _widthDraggedColumn = null;
880
881 /*** true if the header height is beeing dragged. */
882 protected boolean _headerDragged = false;
883
884 /***
885 * Handle mouse double click.
886 *
887 * @param x x coordinate
888 * @param y y coordinate
889 */
890 private void mouseDouble(int x, int y) {
891 if (_tableRect.contains(x, y) || (_fixedColumns > 0 && _fixedColRect.contains(x, y))
892 || (_fixedRows > 0 && _fixedRowRect.contains(x, y))) {
893 setFocus(x, y);
894 startEditing(rowForY(y), colForX(x), (char) 0);
895 }
896 }
897
898 /***
899 * Handle mouse pressed.
900 *
901 * @param x x coordinate
902 * @param y y coordinate
903 * @param popuptrigger true if the button pressed was the popup trigger
904 * @param stateMask statemask from the event
905 */
906 private void mousePressed(int x, int y, boolean popuptrigger, int stateMask) {
907
908 if (_dragMarkerRect != null && _dragMarkerRect.contains(x, y)) {
909 _isFillDrag = true;
910 _firstFillDragSelect = _selectedIdxRectangle;
911 return;
912 }
913
914 IRow row = rowByBottomBorder(y);
915 if (row != null
916 && _rowResizeAllowed
917 && (_tvs.getRowHeigthMode(row) == RowHeightMode.VARIABLE || _tvs.getRowHeigthMode(row) == RowHeightMode.OPTANDVAR)
918 && (!_resizeRestriction || Math.abs(x - _tableRect.x) <= SELDELTA || (_fixedColRect != null && _fixedColRect
919 .contains(x, y)))) {
920 _heightDraggedRowInfo = getRowInfo(row);
921 return;
922 } else {
923 IColumn col = colByRightBorder(x);
924 if (col != null && _columnResizeAllowed && _tvs.columnResizingAllowed(col)
925 && (!_resizeRestriction || _headerRect == null || _headerRect.contains(x, y))) {
926 _widthDraggedColumn = getColInfo(col);
927 return;
928 }
929 }
930
931 if (_headerResizeAllowed && Math.abs(_headerRect.y + _headerRect.height - y) <= SELDELTA) {
932 _headerDragged = true;
933 return;
934 }
935
936 boolean doSelect = true;
937
938 if (_tableRect.contains(x, y) || (_fixedColumns > 0 && _fixedColRect.contains(x, y))
939 || (_fixedRows > 0 && _fixedRowRect.contains(x, y))) {
940 setFocus(x, y);
941 doSelect = !handleEditorSingleClick(x, y);
942 }
943
944 if (_tableRect.contains(x, y) || (_fixedColumns > 0 && _fixedColRect.contains(x, y))
945 || (_fixedRows > 0 && _fixedRowRect.contains(x, y))) {
946 IRow xrow = rowForY(y);
947 IColumn xcol = colForX(x);
948 if (xrow != null && xcol != null && isHierarchyColumn(xrow, xcol)) {
949 Rectangle rect = getCellBounds(xrow, xcol);
950 IHierarchyRenderer hrenderer = (IHierarchyRenderer) getCellRenderer(xrow, xcol);
951 if (hrenderer.isInActiveArea(xrow, rect, x, y)) {
952 toggleExpanded(xrow);
953 }
954 }
955 }
956
957 IColumn xcol = colForX(x);
958 if (_allowSorting && _headerRect.contains(x, y)
959 && _headerRenderer.isSortingClick(getHeaderDrawingArea(xcol), xcol, x, y)) {
960 _tvs.setSorting(xcol);
961 } else if (doSelect) {
962
963 handleSelection(x, y, stateMask, false);
964 }
965 }
966
967 /***
968 * Toggle the expanded state of a row.
969 *
970 * @param row row to toggle
971 */
972 private void toggleExpanded(IRow row) {
973 IHierarchicalTableViewState hvs = (IHierarchicalTableViewState) _tvs;
974 hvs.setExpanded((ITableNode) row, !hvs.isExpanded((ITableNode) row));
975 }
976
977 /***
978 * Determine whether the column is the hierrarchy column. This is accomplished by looking at the cell renderer
979 * class.
980 *
981 * @param row row
982 * @param col column
983 * @return true if hte adressed cell is part of the hierarchy column
984 */
985 public boolean isHierarchyColumn(IRow row, IColumn col) {
986 if (row == null || col == null) {
987 return false;
988 }
989 return getCellRenderer(row, col) instanceof TableHierarchyRenderer;
990 }
991
992 /***
993 * Check whether a column is the hierarchy column.
994 *
995 * @param column column to check
996 * @return <code>true</code> if the column is the hierarchy column
997 */
998 public boolean isHierarchyColumn(IColumn column) {
999 if (column == null) {
1000 return false;
1001 }
1002 return isHierarchyColumn(_rows.get(0), column);
1003 }
1004
1005 /***
1006 * Retrieve the rectangle in which the header of a column is drawn.
1007 *
1008 * @param col column
1009 * @return drawing rectangle for the header
1010 */
1011 private Rectangle getHeaderDrawingArea(IColumn col) {
1012 int x = getColInfo(col).x;
1013 Rectangle r = new Rectangle(x, _tableRect.y, _tvs.getColumnWidth(col), _tableRect.height);
1014 return r;
1015 }
1016
1017 /***
1018 * Check whether a click is a row selection.
1019 *
1020 * @param x x coordinate
1021 * @param y y coordinate
1022 * @return true for click on the left margin of the table or in the fixed column area
1023 */
1024 private boolean isRowSelection(int x, int y) {
1025 return (Math.abs(x - _tableRect.x) <= SELDELTA || (_fixedColRect != null && _fixedColRect.contains(x, y)));
1026 }
1027
1028 /***
1029 * Check whether a click is a column selection.
1030 *
1031 * @param x x coordinate
1032 * @param y y coordinate
1033 * @return true for coordinate in header or fixed rows
1034 */
1035 private boolean isColumnSelection(int x, int y) {
1036 return (_headerRect != null && _headerRect.contains(x, y))
1037 || (_fixedRowRect != null && _fixedRowRect.contains(x, y));
1038 }
1039
1040 protected int _firstCellSelectX = -1;
1041 protected int _firstCellSelectY = -1;
1042 protected int _lastCellSelectX = -1;
1043 protected int _lastCellSelectY = -1;
1044
1045 /***
1046 * marker flag for drag operation: fill drag.
1047 */
1048 protected boolean _isFillDrag = false;
1049
1050 /*** first col selected in drag. */
1051 protected int _firstColSelectIdx = -1;
1052 /*** last col selected in drag or as standard col selection. */
1053 protected int _lastColSelectIdx = -1;
1054 int _lastKeyColSelectIdx = -1;
1055 int _firstKeyColSelectIdx = -1;
1056
1057 /*** first row selected in drag. */
1058 protected int _firstRowSelectIdx = -1;
1059 /*** last row selected in drag. */
1060 protected int _lastRowSelectIdx = -1;
1061 int _lastKeyRowSelectIdx = -1;
1062 int _firstKeyRowSelectIdx = -1;
1063
1064 /*** last cell idx selected by shift-arrow. */
1065 protected Point _lastKeySelect = null;
1066 /*** first cell idx selected by shift-arrow. */
1067 protected Point _firstKeySelect = null;
1068
1069 /*** enum for the selection type (intern). */
1070 private enum SelectType {
1071 NONE, CELL, COLUMN, ROW
1072 };
1073
1074 /*** index rectangle of selected cells whenn a fill drag starts. */
1075 Rectangle _firstFillDragSelect = null;
1076 /*** true if the fill drag is horizontal (fill along the x axis), false for vertical fill drag. */
1077 private boolean _horizontalFillDrag;
1078
1079 /*** type of the last selection, used for handling keyboard selection. */
1080 protected SelectType _lastSelectType = SelectType.NONE;
1081
1082 /***
1083 * Handle selection operations.
1084 *
1085 * @param x x
1086 * @param y y
1087 * @param stateMask key state mask
1088 * @param dragging true when dragging
1089 */
1090 private void handleSelection(int x, int y, int stateMask, boolean dragging) {
1091
1092 _lastKeySelect = null;
1093 _firstKeySelect = null;
1094 _firstKeyColSelectIdx = -1;
1095 _lastKeyColSelectIdx = -1;
1096 _firstKeyRowSelectIdx = -1;
1097 _lastKeyRowSelectIdx = -1;
1098
1099 IRow row = rowForY(y);
1100 int rowIdx = row != null ? _rows.indexOf(row) : -1;
1101 IColumn col = colForX(x);
1102 int colIdx = getColumnIdx(col);
1103
1104
1105 if (dragging && _isFillDrag) {
1106 if (_selectionModel.isCellSelectionAllowed() && _tableRect.contains(x, y)) {
1107 if (col != null && row != null) {
1108 if (_firstCellSelectX == -1) {
1109 _firstCellSelectX = colIdx;
1110 _firstCellSelectY = rowIdx;
1111 }
1112 if (Math.abs(_firstCellSelectX - colIdx) > Math.abs(_firstCellSelectY - rowIdx)) {
1113 rowIdx = _firstCellSelectY;
1114 row = rowForIdx(rowIdx);
1115 _horizontalFillDrag = false;
1116 } else {
1117 colIdx = _firstCellSelectX;
1118 col = colForIdx(colIdx);
1119 _horizontalFillDrag = true;
1120 }
1121 ensureSelectionContainsRegion(_firstFillDragSelect, colIdx, rowIdx, _lastCellSelectX,
1122 _lastCellSelectY);
1123
1124
1125 _lastCellSelectX = colIdx;
1126 _lastCellSelectY = rowIdx;
1127
1128
1129 setFocus(row, col);
1130 }
1131 }
1132
1133 return;
1134 }
1135
1136
1137 if (row != null && _selectionModel.isFullRowSelectionAllowed()
1138 && (isRowSelection(x, y) || _selectionModel.isOnlyRowSelectionAllowed() || _firstRowSelectIdx != -1)) {
1139 if (_firstRowSelectIdx == -1) {
1140 _firstRowSelectIdx = rowIdx;
1141 }
1142 if ((stateMask & SWT.CONTROL) != 0) {
1143 if (!_selectionModel.getSelection().getSelectedRows().contains(row)) {
1144 _selectionModel.addSelectedRow(row);
1145 } else {
1146 _selectionModel.remSelectedRow(row);
1147 }
1148 _lastSelectType = SelectType.ROW;
1149 } else if (dragging) {
1150 ensureSelectionContainsRowRegion(_firstRowSelectIdx, rowIdx, _lastRowSelectIdx);
1151 _lastRowSelectIdx = rowIdx;
1152 _lastSelectType = SelectType.ROW;
1153 } else {
1154 _selectionModel.clearSelection();
1155 _selectionModel.addSelectedRow(row);
1156 _lastSelectType = SelectType.ROW;
1157 }
1158 _lastRowSelectIdx = rowIdx;
1159 return;
1160 }
1161
1162 if (_selectionModel.isFullColumnSelectionAllowed() && (isColumnSelection(x, y) || _firstColSelectIdx != -1)) {
1163 if (_firstColSelectIdx == -1) {
1164 _firstColSelectIdx = colIdx;
1165 }
1166 if ((stateMask & SWT.CONTROL) != 0) {
1167 if (!_selectionModel.getSelection().getSelectedColumns().contains(col)) {
1168 _selectionModel.addSelectedColumn(col);
1169 } else {
1170 _selectionModel.remSelectedColumn(col);
1171 }
1172 _lastSelectType = SelectType.COLUMN;
1173 } else if (dragging) {
1174 ensureSelectionContainsColRegion(_firstColSelectIdx, colIdx, _lastColSelectIdx);
1175 _lastColSelectIdx = colIdx;
1176 _lastSelectType = SelectType.COLUMN;
1177 } else {
1178 _selectionModel.clearSelection();
1179 _selectionModel.addSelectedColumn(col);
1180 _lastSelectType = SelectType.COLUMN;
1181 }
1182 _lastColSelectIdx = colIdx;
1183 return;
1184 }
1185
1186 if (_selectionModel.isCellSelectionAllowed() && _tableRect.contains(x, y)) {
1187 if (col != null && row != null) {
1188 IJaretTableCell cell = new JaretTableCellImpl(row, col);
1189 if (_firstCellSelectX == -1) {
1190 _firstCellSelectX = colIdx;
1191 _firstCellSelectY = rowIdx;
1192 }
1193 if ((stateMask & SWT.CONTROL) != 0) {
1194 if (!_selectionModel.getSelection().getSelectedCells().contains(cell)) {
1195 _selectionModel.addSelectedCell(cell);
1196 } else {
1197 _selectionModel.remSelectedCell(cell);
1198 }
1199 _lastSelectType = SelectType.CELL;
1200 } else if (dragging) {
1201 ensureSelectionContainsRegion(_firstCellSelectX, _firstCellSelectY, colIdx, rowIdx,
1202 _lastCellSelectX, _lastCellSelectY);
1203 _lastCellSelectX = colIdx;
1204 _lastCellSelectY = rowIdx;
1205 _lastSelectType = SelectType.CELL;
1206 } else {
1207 _selectionModel.clearSelection();
1208 _selectionModel.addSelectedCell(cell);
1209 _lastSelectType = SelectType.CELL;
1210 }
1211
1212
1213 setFocus(row, col);
1214 }
1215 }
1216
1217 }
1218
1219 /***
1220 * Ensures that the selection contains the rows from firstIdx to rowIdx. If the range firstIdx to lastIdx is larger
1221 * than the region the other rows will be removed from the selection.
1222 *
1223 * @param firstRowSelectIdx first selected row index
1224 * @param rowIdx current selected row index
1225 * @param lastRowSelectIdx may be -1 for no last selection
1226 */
1227 private void ensureSelectionContainsRowRegion(int firstRowSelectIdx, int rowIdx, int lastRowSelectIdx) {
1228 int first = Math.min(firstRowSelectIdx, rowIdx);
1229 int end = Math.max(firstRowSelectIdx, rowIdx);
1230 for (int i = first; i <= end; i++) {
1231 IRow row = rowForIdx(i);
1232 if (!_selectionModel.getSelection().getSelectedRows().contains(row)) {
1233 _selectionModel.addSelectedRow(row);
1234 }
1235 }
1236 if (lastRowSelectIdx != -1) {
1237 int f = Math.min(firstRowSelectIdx, lastRowSelectIdx);
1238 int e = Math.max(firstRowSelectIdx, lastRowSelectIdx);
1239 for (int i = f; i <= e; i++) {
1240 if (i < first || i > end) {
1241 IRow row = rowForIdx(i);
1242 _selectionModel.remSelectedRow(row);
1243 }
1244 }
1245 }
1246 }
1247
1248 /***
1249 * Ensures that the selection contains the columns from firstIdx to colIdx. If the range firstIdx to lastIdx is
1250 * larger than the region the other columns will be removed from the selection.
1251 *
1252 * @param firstColIdx first selected column index
1253 * @param colIdx current selected column index
1254 * @param lastColSelectIdx may be -1 for no last selection
1255 */
1256 private void ensureSelectionContainsColRegion(int firstColIdx, int colIdx, int lastColSelectIdx) {
1257 int first = Math.min(firstColIdx, colIdx);
1258 int end = Math.max(firstColIdx, colIdx);
1259 for (int i = first; i <= end; i++) {
1260 IColumn col = colForIdx(i);
1261 if (!_selectionModel.getSelection().getSelectedColumns().contains(col)) {
1262 _selectionModel.addSelectedColumn(col);
1263 }
1264 }
1265 if (lastColSelectIdx != -1) {
1266 int f = Math.min(firstColIdx, lastColSelectIdx);
1267 int e = Math.max(firstColIdx, lastColSelectIdx);
1268 for (int i = f; i <= e; i++) {
1269 if (i < first || i > end) {
1270 IColumn col = colForIdx(i);
1271 _selectionModel.remSelectedColumn(col);
1272 }
1273 }
1274 }
1275 }
1276
1277 /***
1278 * Ensures the selection contains the cells in the rectangle given by first*, *Idx. If the rectangle given by
1279 * first*, last* is larger than the other rectangle is is ensured that the additional cells are not in the
1280 * selection.
1281 *
1282 * @param firstCellSelectX begin x index of selected cell rectangle
1283 * @param firstCellSelectY begin y index of selected cell rectangle
1284 * @param colIdx end x index of selected cell rectangle
1285 * @param rowIdx end y index of selected cell rectangle
1286 * @param lastCellSelectX may be -1 for no last selection
1287 * @param lastCellSelectY may be -1 for no last selection
1288 */
1289 private void ensureSelectionContainsRegion(int firstCellSelectX, int firstCellSelectY, int colIdx, int rowIdx,
1290 int lastCellSelectX, int lastCellSelectY) {
1291 int firstx = Math.min(firstCellSelectX, colIdx);
1292 int endx = Math.max(firstCellSelectX, colIdx);
1293 int firsty = Math.min(firstCellSelectY, rowIdx);
1294 int endy = Math.max(firstCellSelectY, rowIdx);
1295
1296 for (int x = firstx; x <= endx; x++) {
1297 for (int y = firsty; y <= endy; y++) {
1298 IJaretTableCell cell = new JaretTableCellImpl(rowForIdx(y), colForIdx(x));
1299 if (!_selectionModel.getSelection().getSelectedCells().contains(cell)) {
1300 _selectionModel.addSelectedCell(cell);
1301 }
1302 }
1303 }
1304
1305
1306 if (lastCellSelectX != -1) {
1307 int lfx = Math.min(firstCellSelectX, lastCellSelectX);
1308 int lex = Math.max(firstCellSelectX, lastCellSelectX);
1309 int lfy = Math.min(firstCellSelectY, lastCellSelectY);
1310 int ley = Math.max(firstCellSelectY, lastCellSelectY);
1311
1312 for (int x = lfx; x <= lex; x++) {
1313 for (int y = lfy; y <= ley; y++) {
1314 if (!(x >= firstx && x <= endx && y >= firsty && y <= endy)) {
1315 IJaretTableCell cell = new JaretTableCellImpl(rowForIdx(y), colForIdx(x));
1316 _selectionModel.remSelectedCell(cell);
1317 }
1318 }
1319 }
1320 }
1321
1322 }
1323
1324 /***
1325 * Ensures the selection contains the cells in the rectangle given by firstIdxRect, *Idx. If the rectangle given by
1326 * first*, last* is larger than the other rectangle is is ensured that the additional cells are not in the
1327 * selection.
1328 *
1329 * @param firstIdxRect rectangle containing the indizes of the originating rect
1330 * @param colIdx new end x index for the selected rectangle
1331 * @param rowIdx new end y index for teh selecetd rectangle
1332 * @param lastCellSelectX may be -1 for no last selection
1333 * @param lastCellSelectY may be -1 for no last selection
1334 */
1335 private void ensureSelectionContainsRegion(Rectangle firstIdxRect, int colIdx, int rowIdx, int lastCellSelectX,
1336 int lastCellSelectY) {
1337
1338 int firstx = Math.min(firstIdxRect.x, colIdx);
1339 int endx = Math.max(firstIdxRect.x + firstIdxRect.width - 1, colIdx);
1340 int firsty = Math.min(firstIdxRect.y, rowIdx);
1341 int endy = Math.max(firstIdxRect.y + firstIdxRect.height - 1, rowIdx);
1342
1343 for (int x = firstx; x <= endx; x++) {
1344 for (int y = firsty; y <= endy; y++) {
1345 IJaretTableCell cell = new JaretTableCellImpl(rowForIdx(y), colForIdx(x));
1346 if (!_selectionModel.getSelection().getSelectedCells().contains(cell)) {
1347 _selectionModel.addSelectedCell(cell);
1348 }
1349 }
1350 }
1351
1352
1353 if (lastCellSelectX != -1 && lastCellSelectY != -1) {
1354 int lfx = Math.min(firstIdxRect.x, lastCellSelectX);
1355 int lex = Math.max(firstIdxRect.x + firstIdxRect.width - 1, lastCellSelectX);
1356 int lfy = Math.min(firstIdxRect.y, lastCellSelectY);
1357 int ley = Math.max(firstIdxRect.y + firstIdxRect.height - 1, lastCellSelectY);
1358
1359 for (int x = lfx; x <= lex; x++) {
1360 for (int y = lfy; y <= ley; y++) {
1361 if (!(x >= firstx && x <= endx && y >= firsty && y <= endy)) {
1362 IJaretTableCell cell = new JaretTableCellImpl(rowForIdx(y), colForIdx(x));
1363 _selectionModel.remSelectedCell(cell);
1364 }
1365 }
1366 }
1367 }
1368
1369 }
1370
1371 /***
1372 * Handle drag of mouse.
1373 *
1374 * @param x x coordinate of pointer
1375 * @param y y coordinate of pointer
1376 * @param stateMask keyStatemask
1377 */
1378 private void mouseDragged(int x, int y, int stateMask) {
1379 if (_isFillDrag) {
1380 handleSelection(x, y, stateMask, true);
1381 } else if (_heightDraggedRowInfo != null) {
1382 int newHeight = y - _heightDraggedRowInfo.y;
1383 if (newHeight < _tvs.getMinimalRowHeight()) {
1384 newHeight = _tvs.getMinimalRowHeight();
1385 }
1386 _tvs.setRowHeight(_heightDraggedRowInfo.row, newHeight);
1387
1388 if (_tvs.getRowHeigthMode(_heightDraggedRowInfo.row) == RowHeightMode.OPTANDVAR) {
1389 _tvs.setRowHeightMode(_heightDraggedRowInfo.row, RowHeightMode.VARIABLE);
1390 }
1391
1392 } else if (_widthDraggedColumn != null) {
1393 int newWidth = x - _widthDraggedColumn.x;
1394 if (newWidth < _tvs.getMinimalColWidth()) {
1395 newWidth = _tvs.getMinimalColWidth();
1396 }
1397 if (_tvs.getColumnWidth(_widthDraggedColumn.column) != newWidth) {
1398 _tvs.setColumnWidth(_widthDraggedColumn.column, newWidth);
1399 }
1400 } else if (_headerDragged) {
1401 int newHeight = y - _headerRect.y;
1402 if (newHeight < _minHeaderHeight) {
1403 newHeight = _minHeaderHeight;
1404 }
1405 setHeaderHeight(newHeight);
1406 } else {
1407 handleSelection(x, y, stateMask, true);
1408 }
1409 }
1410
1411 /***
1412 * Handle mouse move. This is mostly: modifying the appearance of the cursor.
1413 *
1414 * @param x x coordinate of pointer
1415 * @param y y coordinate of pointer
1416 * @param stateMask keyStatemask
1417 */
1418 private void mouseMoved(int x, int y, int stateMask) {
1419 Display display = Display.getCurrent();
1420 Shell activeShell = display != null ? display.getActiveShell() : null;
1421
1422
1423 if (_dragMarkerRect != null && _dragMarkerRect.contains(x, y)) {
1424 if (activeShell != null) {
1425
1426 activeShell.setCursor(display.getSystemCursor(SWT.CURSOR_SIZEALL));
1427 }
1428 return;
1429 }
1430
1431
1432 IRow row = rowByBottomBorder(y);
1433 if (row != null
1434 && _rowResizeAllowed
1435 && (_tvs.getRowHeigthMode(row) == RowHeightMode.VARIABLE || _tvs.getRowHeigthMode(row) == RowHeightMode.OPTANDVAR)
1436 && (!_resizeRestriction || Math.abs(x - _tableRect.x) <= SELDELTA || (_fixedColRect != null && _fixedColRect
1437 .contains(x, y)))) {
1438 if (activeShell != null) {
1439 activeShell.setCursor(display.getSystemCursor(SWT.CURSOR_SIZENS));
1440 }
1441 return;
1442 } else {
1443 IColumn col = colByRightBorder(x);
1444 if (col != null && _columnResizeAllowed && _tvs.columnResizingAllowed(col)
1445 && (!_resizeRestriction || _headerRect == null || _headerRect.contains(x, y))) {
1446 if (activeShell != null) {
1447 activeShell.setCursor(display.getSystemCursor(SWT.CURSOR_SIZEW));
1448 }
1449 return;
1450 }
1451 }
1452
1453 if (_headerRect != null && _headerResizeAllowed && Math.abs(_headerRect.y + _headerRect.height - y) <= SELDELTA) {
1454 if (activeShell != null) {
1455 activeShell.setCursor(display.getSystemCursor(SWT.CURSOR_SIZENS));
1456 }
1457 return;
1458 }
1459
1460 if (Display.getCurrent().getActiveShell() != null) {
1461 Display.getCurrent().getActiveShell().setCursor(Display.getCurrent().getSystemCursor(SWT.CURSOR_ARROW));
1462 }
1463
1464 }
1465
1466 /***
1467 * Handle the release of a mouse button.
1468 *
1469 * @param x x coordinate
1470 * @param y y coordinate
1471 * @param popUpTrigger true if the buttonm is the popup trigger
1472 */
1473 private void mouseReleased(int x, int y, boolean popUpTrigger) {
1474
1475 if (_isFillDrag) {
1476 handleFill();
1477 }
1478
1479 _heightDraggedRowInfo = null;
1480 _widthDraggedColumn = null;
1481 _headerDragged = false;
1482
1483 _firstCellSelectX = -1;
1484 _firstCellSelectY = -1;
1485 _lastCellSelectX = -1;
1486 _lastCellSelectY = -1;
1487
1488 _firstColSelectIdx = -1;
1489
1490 _firstRowSelectIdx = -1;
1491
1492
1493 _isFillDrag = false;
1494
1495 if (_headerRect.contains(x, y) && popUpTrigger) {
1496 displayHeaderContextMenu(x, y);
1497 } else if (popUpTrigger && isRowSelection(x, y)) {
1498 displayRowContextMenu(x, y);
1499 }
1500
1501 }
1502
1503 /***
1504 * Handle the end of a fill drag.
1505 *
1506 */
1507 private void handleFill() {
1508 if (_fillDragStrategy != null) {
1509 if (_horizontalFillDrag) {
1510
1511 for (int i = _firstFillDragSelect.x; i < _firstFillDragSelect.x + _firstFillDragSelect.width; i++) {
1512 IJaretTableCell firstCell = getCellForIdx(i, _firstFillDragSelect.y);
1513 List<IJaretTableCell> cells = getSelectedCellsVertical(i);
1514 cells.remove(firstCell);
1515 _fillDragStrategy.doFill(this, firstCell, cells);
1516 }
1517 } else {
1518
1519 for (int i = _firstFillDragSelect.y; i < _firstFillDragSelect.y + _firstFillDragSelect.height; i++) {
1520 IJaretTableCell firstCell = getCellForIdx(_firstFillDragSelect.x, i);
1521 List<IJaretTableCell> cells = getSelectedCellsHorizontal(i);
1522 cells.remove(firstCell);
1523 _fillDragStrategy.doFill(this, firstCell, cells);
1524 }
1525 }
1526 }
1527 }
1528
1529 /***
1530 * Get all cells that are selected at the x idx given.
1531 *
1532 * @param x x idx
1533 * @return list of selecetd cells with x idx == x
1534 */
1535 private List<IJaretTableCell> getSelectedCellsVertical(int x) {
1536 List<IJaretTableCell> cells = new ArrayList<IJaretTableCell>();
1537 List<IJaretTableCell> s = getSelectionModel().getSelection().getSelectedCells();
1538 for (IJaretTableCell cell : s) {
1539 Point p = getCellDisplayIdx(cell);
1540 if (p.x == x) {
1541 cells.add(cell);
1542 }
1543 }
1544 return cells;
1545 }
1546
1547 /***
1548 * Get all cells that are selected at the y idx given.
1549 *
1550 * @param y y idx
1551 * @return list of selecetd cells at idx y == y
1552 */
1553 private List<IJaretTableCell> getSelectedCellsHorizontal(int y) {
1554 List<IJaretTableCell> cells = new ArrayList<IJaretTableCell>();
1555 List<IJaretTableCell> s = getSelectionModel().getSelection().getSelectedCells();
1556 for (IJaretTableCell cell : s) {
1557 Point p = getCellDisplayIdx(cell);
1558 if (p.y == y) {
1559 cells.add(cell);
1560 }
1561 }
1562 return cells;
1563 }
1564
1565 /***
1566 * Supply tooltip text for a position in the table.
1567 *
1568 * @param x x coordinate
1569 * @param y y coordinate
1570 * @return tooltip text or <code>null</code>
1571 */
1572 private String getToolTipText(int x, int y) {
1573 IJaretTableCell cell = getCell(x, y);
1574 if (cell != null) {
1575 Rectangle bounds = getCellBounds(cell);
1576 ICellRenderer renderer = getCellRenderer(cell.getRow(), cell.getColumn());
1577 if (renderer != null) {
1578 String tt = renderer.getTooltip(this, bounds, cell.getRow(), cell.getColumn(), x, y);
1579 if (tt != null) {
1580 return tt;
1581 }
1582 }
1583 }
1584 return null;
1585 }
1586
1587
1588
1589
1590 /***
1591 * Handle any key presses.
1592 *
1593 * @param event key event
1594 */
1595 private void handleKeyPressed(KeyEvent event) {
1596 if ((event.stateMask & SWT.SHIFT) != 0 && Character.isISOControl(event.character)) {
1597 switch (event.keyCode) {
1598 case SWT.ARROW_RIGHT:
1599 selectRight();
1600 break;
1601 case SWT.ARROW_LEFT:
1602 selectLeft();
1603 break;
1604 case SWT.ARROW_DOWN:
1605 selectDown();
1606 break;
1607 case SWT.ARROW_UP:
1608 selectUp();
1609 break;
1610
1611 default:
1612
1613 break;
1614 }
1615 } else if ((event.stateMask & SWT.CONTROL) != 0 && Character.isISOControl(event.character)) {
1616
1617
1618 switch (event.keyCode) {
1619 case 'c':
1620 copy();
1621 break;
1622 case 'x':
1623 cut();
1624 break;
1625 case 'v':
1626 paste();
1627 break;
1628 case 'a':
1629 selectAll();
1630 break;
1631
1632 default:
1633
1634 break;
1635 }
1636
1637 } else {
1638 _lastKeySelect = null;
1639 _firstKeySelect = null;
1640
1641 switch (event.keyCode) {
1642 case SWT.ARROW_RIGHT:
1643 focusRight();
1644 break;
1645 case SWT.ARROW_LEFT:
1646 focusLeft();
1647 break;
1648 case SWT.ARROW_DOWN:
1649 focusDown();
1650 break;
1651 case SWT.ARROW_UP:
1652 focusUp();
1653 break;
1654 case SWT.TAB:
1655 focusRight();
1656 break;
1657 case SWT.F2:
1658 startEditing(_focussedRow, _focussedColumn, (char) 0);
1659 break;
1660
1661 default:
1662 if (event.character == ' ' && isHierarchyColumn(_focussedRow, _focussedColumn)) {
1663 toggleExpanded(_focussedRow);
1664 } else if (!Character.isISOControl(event.character)) {
1665 startEditing(event.character);
1666 }
1667
1668 break;
1669 }
1670 }
1671
1672 }
1673
1674 /***
1675 * Enlarge selection to the right.
1676 */
1677 private void selectRight() {
1678 IJaretTableCell cell = getFocussedCell();
1679 if (_lastSelectType == SelectType.CELL && cell != null) {
1680 int cx = getColumnIdx(cell.getColumn());
1681 int cy = getRowIdx(cell.getRow());
1682 if (_lastKeySelect == null) {
1683 _lastKeySelect = new Point(-1, -1);
1684 }
1685 if (_firstKeySelect == null) {
1686 _firstKeySelect = new Point(cx, cy);
1687 _selectionModel.clearSelection();
1688 }
1689
1690 focusRight();
1691 cell = getFocussedCell();
1692 cx = getColumnIdx(cell.getColumn());
1693 cy = getRowIdx(cell.getRow());
1694
1695 ensureSelectionContainsRegion(_firstKeySelect.x, _firstKeySelect.y, cx, cy, _lastKeySelect.x,
1696 _lastKeySelect.y);
1697
1698 _lastSelectType = SelectType.CELL;
1699 _lastKeySelect = new Point(cx, cy);
1700 } else if (_lastSelectType == SelectType.COLUMN && (_lastColSelectIdx != -1 || _lastKeyColSelectIdx != -1)) {
1701 if (_firstKeyColSelectIdx == -1) {
1702 _firstKeyColSelectIdx = _lastColSelectIdx;
1703 }
1704 int colIdx = _lastKeyColSelectIdx != -1 ? _lastKeyColSelectIdx + 1 : _firstKeyColSelectIdx + 1;
1705 if (colIdx > _cols.size() - 1) {
1706 colIdx = _cols.size() - 1;
1707 }
1708 ensureSelectionContainsColRegion(_firstKeyColSelectIdx, colIdx, _lastKeyColSelectIdx);
1709 _lastKeyColSelectIdx = colIdx;
1710 }
1711 }
1712
1713 /***
1714 * Enlarge selection to the left.
1715 */
1716 private void selectLeft() {
1717 IJaretTableCell cell = getFocussedCell();
1718 if (_lastSelectType == SelectType.CELL && cell != null) {
1719 if (cell != null) {
1720 int cx = getColumnIdx(cell.getColumn());
1721 int cy = getRowIdx(cell.getRow());
1722 if (_lastKeySelect == null) {
1723 _lastKeySelect = new Point(-1, -1);
1724 }
1725 if (_firstKeySelect == null) {
1726 _firstKeySelect = new Point(cx, cy);
1727 _selectionModel.clearSelection();
1728 }
1729
1730 focusLeft();
1731
1732 cell = getFocussedCell();
1733 cx = getColumnIdx(cell.getColumn());
1734 cy = getRowIdx(cell.getRow());
1735
1736 ensureSelectionContainsRegion(_firstKeySelect.x, _firstKeySelect.y, cx, cy, _lastKeySelect.x,
1737 _lastKeySelect.y);
1738 _lastSelectType = SelectType.CELL;
1739 _lastKeySelect = new Point(cx, cy);
1740 }
1741 } else if (_lastSelectType == SelectType.COLUMN && (_lastColSelectIdx != -1 || _lastKeyColSelectIdx != -1)) {
1742 if (_firstKeyColSelectIdx == -1) {
1743 _firstKeyColSelectIdx = _lastColSelectIdx;
1744 }
1745 int colIdx = _lastKeyColSelectIdx != -1 ? _lastKeyColSelectIdx - 1 : _firstKeyColSelectIdx - 1;
1746 if (colIdx < 0) {
1747 colIdx = 0;
1748 }
1749 ensureSelectionContainsColRegion(_firstKeyColSelectIdx, colIdx, _lastKeyColSelectIdx);
1750 _lastKeyColSelectIdx = colIdx;
1751 }
1752 }
1753
1754 /***
1755 * Enlarge selection downwards.
1756 */
1757 private void selectDown() {
1758 IJaretTableCell cell = getFocussedCell();
1759 if (_lastSelectType == SelectType.CELL && cell != null) {
1760 if (cell != null) {
1761 int cx = getColumnIdx(cell.getColumn());
1762 int cy = getRowIdx(cell.getRow());
1763 if (_lastKeySelect == null) {
1764 _lastKeySelect = new Point(-1, -1);
1765 }
1766 if (_firstKeySelect == null) {
1767 _firstKeySelect = new Point(cx, cy);
1768 _selectionModel.clearSelection();
1769 }
1770
1771 focusDown();
1772
1773 cell = getFocussedCell();
1774 cx = getColumnIdx(cell.getColumn());
1775 cy = getRowIdx(cell.getRow());
1776
1777 ensureSelectionContainsRegion(_firstKeySelect.x, _firstKeySelect.y, cx, cy, _lastKeySelect.x,
1778 _lastKeySelect.y);
1779
1780 _lastSelectType = SelectType.CELL;
1781 _lastKeySelect = new Point(cx, cy);
1782 }
1783 } else if (_lastSelectType == SelectType.ROW && (_lastRowSelectIdx != -1 || _lastKeyRowSelectIdx != -1)) {
1784 if (_firstKeyRowSelectIdx == -1) {
1785 _firstKeyRowSelectIdx = _lastRowSelectIdx;
1786 }
1787 int rowIdx = _lastKeyRowSelectIdx != -1 ? _lastKeyRowSelectIdx + 1 : _firstKeyRowSelectIdx + 1;
1788 if (rowIdx > _rows.size() - 1) {
1789 rowIdx = _rows.size() - 1;
1790 }
1791 ensureSelectionContainsRowRegion(_firstKeyRowSelectIdx, rowIdx, _lastKeyRowSelectIdx);
1792 _lastKeyRowSelectIdx = rowIdx;
1793 }
1794
1795 }
1796
1797 /***
1798 * Enlarge selection upwards.
1799 */
1800 private void selectUp() {
1801 IJaretTableCell cell = getFocussedCell();
1802 if (_lastSelectType == SelectType.CELL && cell != null) {
1803 if (cell != null) {
1804 int cx = getColumnIdx(cell.getColumn());
1805 int cy = getRowIdx(cell.getRow());
1806 if (_lastKeySelect == null) {
1807 _lastKeySelect = new Point(-1, -1);
1808 }
1809 if (_firstKeySelect == null) {
1810 _firstKeySelect = new Point(cx, cy);
1811 _selectionModel.clearSelection();
1812 }
1813
1814 focusUp();
1815
1816 cell = getFocussedCell();
1817 cx = getColumnIdx(cell.getColumn());
1818 cy = getRowIdx(cell.getRow());
1819
1820 ensureSelectionContainsRegion(_firstKeySelect.x, _firstKeySelect.y, cx, cy, _lastKeySelect.x,
1821 _lastKeySelect.y);
1822
1823 _lastSelectType = SelectType.CELL;
1824 _lastKeySelect = new Point(cx, cy);
1825 }
1826 } else if (_lastSelectType == SelectType.ROW && (_lastRowSelectIdx != -1 || _lastKeyRowSelectIdx != -1)) {
1827 if (_firstKeyRowSelectIdx == -1) {
1828 _firstKeyRowSelectIdx = _lastRowSelectIdx;
1829 }
1830 int rowIdx = _lastKeyRowSelectIdx != -1 ? _lastKeyRowSelectIdx - 1 : _firstKeyRowSelectIdx - 1;
1831 if (rowIdx < 0) {
1832 rowIdx = 0;
1833 }
1834 ensureSelectionContainsRowRegion(_firstKeyRowSelectIdx, rowIdx, _lastKeyRowSelectIdx);
1835 _lastKeyRowSelectIdx = rowIdx;
1836 }
1837 }
1838
1839 /***
1840 * Retrieve the currently focussed cell.
1841 *
1842 * @return the focussed cell or <code>null</code> if no cell is focussed
1843 */
1844 public IJaretTableCell getFocussedCell() {
1845 if (_focussedColumn != null && _focussedRow != null) {
1846 return new JaretTableCellImpl(_focussedRow, _focussedColumn);
1847 }
1848 return null;
1849 }
1850
1851 /***
1852 * Retrieve the indizes of the currently focussed cell (idx in the filtered, sorted or whatever table).
1853 *
1854 * @return Point x = column, y = row or <code>null</code> if no cell is focussed
1855 */
1856 public Point getFocussedCellIdx() {
1857 if (_focussedColumn != null && _focussedRow != null) {
1858 return new Point(getColumnIdx(_focussedColumn), getRowIdx(_focussedRow));
1859 }
1860 return null;
1861 }
1862
1863 /***
1864 * Retrieve the display coordinates for a table cell.
1865 *
1866 * @param cell cell to get he coordinates for
1867 * @return Point x = colIdx, y = rowIdx
1868 */
1869 public Point getCellDisplayIdx(IJaretTableCell cell) {
1870 return new Point(getColumnIdx(cell.getColumn()), getRowIdx(cell.getRow()));
1871 }
1872
1873 /***
1874 * Convenience method for setting a value at a displayed position in the table. NOTE: this method does call the the
1875 * set method of the model directly, so be aware that the model may protest by throwing a runtime exception or just
1876 * ignore the new value.
1877 *
1878 * @param colIdx column index
1879 * @param rowIdx row index
1880 * @param value value to set
1881 */
1882 public void setValue(int colIdx, int rowIdx, Object value) {
1883 IColumn col = getColumn(colIdx);
1884 IRow row = getRow(rowIdx);
1885 col.setValue(row, value);
1886 }
1887
1888 /***
1889 * {@inheritDoc} will get call to transfer focus to the table. The mthod will focus the left/uppermost cell
1890 * displayed. If no rows and columns are present no cell will get the focus.
1891 */
1892 public boolean setFocus() {
1893 super.setFocus();
1894 if (_focussedRow == null && _rows != null && _rows.size() > 0 && _cols.size() > 0) {
1895 setFocus(_rows.get(_firstRowIdx), _cols.get(_firstColIdx));
1896 }
1897 return true;
1898 }
1899
1900 /***
1901 * Ensures there is a focussed cell and uses the cell at 0,0 if no cell is focussed.
1902 *
1903 */
1904 private void ensureFocus() {
1905 if (_focussedRow == null) {
1906 _focussedRow = _rows.get(0);
1907 }
1908 if (_focussedColumn == null) {
1909 _focussedColumn = _cols.get(0);
1910 }
1911 }
1912
1913 /***
1914 * Move the focus left.
1915 */
1916 public void focusLeft() {
1917 ensureFocus();
1918 int idx = _cols.indexOf(_focussedColumn);
1919 if (idx > 0) {
1920 setFocus(_focussedRow, _cols.get(idx - 1));
1921 }
1922 }
1923
1924 /***
1925 * Move the focus right.
1926 */
1927 public void focusRight() {
1928 ensureFocus();
1929 int idx = _cols.indexOf(_focussedColumn);
1930 if (idx < _cols.size() - 1) {
1931 setFocus(_focussedRow, _cols.get(idx + 1));
1932 }
1933 }
1934
1935 /***
1936 * Move the focus up.
1937 */
1938 public void focusUp() {
1939 ensureFocus();
1940 int idx = _rows.indexOf(_focussedRow);
1941 if (idx > 0) {
1942 setFocus(_rows.get(idx - 1), _focussedColumn);
1943 }
1944 }
1945
1946 /***
1947 * Move the focus down.
1948 */
1949 public void focusDown() {
1950 ensureFocus();
1951 int idx = _rows.indexOf(_focussedRow);
1952 if (idx < _rows.size() - 1) {
1953 setFocus(_rows.get(idx + 1), _focussedColumn);
1954 }
1955 }
1956
1957 /***
1958 * Set the focussed cell by coordinates.
1959 *
1960 * @param x x coordinate
1961 * @param y y coordinate
1962 */
1963 private void setFocus(int x, int y) {
1964 IRow row = rowForY(y);
1965 IColumn col = colForX(x);
1966 if (col != null && row != null) {
1967 setFocus(row, col);
1968 }
1969 }
1970
1971 /***
1972 * Check whether editing of a cell is in progress.
1973 *
1974 * @return true when editing a cell
1975 */
1976 public boolean isEditing() {
1977 return _editor != null;
1978 }
1979
1980 /***
1981 * Handle a single mouseclick by passing it to the cell editor if present.
1982 *
1983 * @param x x coordinate of the click
1984 * @param y y coordinate of the click
1985 * @return true if the editor handled the click
1986 */
1987 private boolean handleEditorSingleClick(int x, int y) {
1988 IRow row = rowForY(y);
1989 IColumn col = colForX(x);
1990 if (col != null && row != null) {
1991 ICellEditor editor = getCellEditor(row, col);
1992 if (editor != null) {
1993 Rectangle area = getCellBounds(row, col);
1994 return editor.handleClick(this, row, col, area, x, y);
1995 }
1996 }
1997 return false;
1998 }
1999
2000 /***
2001 * Start editing after a keystroke on a cell.
2002 *
2003 * @param typedKey the typed key
2004 */
2005 private void startEditing(char typedKey) {
2006 if (_focussedRow != null && _focussedColumn != null) {
2007 startEditing(_focussedRow, _focussedColumn, typedKey);
2008 }
2009 }
2010
2011 /***
2012 * Start editing of a specified cell if it is editable.
2013 *
2014 * @param row row
2015 * @param col column
2016 * @param typedKey key typed
2017 */
2018 public void startEditing(IRow row, IColumn col, char typedKey) {
2019 if (isEditing()) {
2020 stopEditing(true);
2021 }
2022 if (!_model.isEditable(row, col)) {
2023 return;
2024 }
2025 clearSelection();
2026 if (row != null && col != null) {
2027 _editor = getCellEditor(row, col);
2028 if (_editor != null) {
2029 _editorControl = _editor.getEditorControl(this, row, col, typedKey);
2030 if (_editorControl != null) {
2031 Rectangle bounds = getCellBounds(row, col);
2032
2033 bounds.x += 1;
2034 bounds.width -= 1;
2035 bounds.y += 1;
2036 if (_editor.getPreferredHeight() == -1 || _editor.getPreferredHeight() < bounds.height) {
2037 bounds.height -= 1;
2038 } else {
2039 bounds.height = _editor.getPreferredHeight();
2040 }
2041 _editorControl.setBounds(bounds);
2042 _editorControl.setVisible(true);
2043 _editorControl.forceFocus();
2044 }
2045 _editorRow = row;
2046 } else {
2047
2048 }
2049 }
2050 if (_editorControl == null) {
2051 stopEditing(true);
2052 }
2053 }
2054
2055 /***
2056 * Clear the selection.
2057 */
2058 private void clearSelection() {
2059 _selectionModel.clearSelection();
2060 }
2061
2062 /***
2063 * Stop editing if in progress.
2064 *
2065 * @param storeValue if true the value of the editor is stored.
2066 */
2067 public void stopEditing(boolean storeValue) {
2068 if (isEditing()) {
2069 _editor.stopEditing(storeValue);
2070 if (storeValue && (_tvs.getRowHeigthMode(_editorRow) == ITableViewState.RowHeightMode.OPTIMAL)
2071 || _tvs.getRowHeigthMode(_editorRow) == ITableViewState.RowHeightMode.OPTANDVAR) {
2072 optimizeHeight(_editorRow);
2073 }
2074 _editorRow = null;
2075 _editor = null;
2076 }
2077 }
2078
2079 /***
2080 * Set the focussed cell.
2081 *
2082 * @param row row
2083 * @param col column
2084 */
2085 private void setFocus(IRow row, IColumn col) {
2086 if (_focussedRow != row || _focussedColumn != col) {
2087 IRow oldRow = _focussedRow;
2088 IColumn oldCol = _focussedColumn;
2089
2090 _focussedRow = row;
2091 _focussedColumn = col;
2092
2093 if (isCompleteVisible(_focussedRow, _focussedColumn)) {
2094 redraw(_focussedRow, _focussedColumn);
2095 if (oldRow != null && oldCol != null) {
2096 redraw(oldRow, oldCol);
2097 }
2098 } else {
2099 scrollToVisible(_focussedRow, _focussedColumn);
2100 }
2101 fireTableFocusChanged(row, col);
2102 }
2103 }
2104
2105 /***
2106 * Calculate the preferred height of a row. Only visibl columns are taken into account.
2107 *
2108 * @param gc Graphics context
2109 * @param row row to calculate the height for
2110 * @return preferred height or -1 if no preferred height can be determined
2111 */
2112 private int getPreferredRowHeight(GC gc, IRow row) {
2113 int result = -1;
2114 for (IColumn column : _cols) {
2115 if (_tvs.getColumnVisible(column)) {
2116 ICellRenderer renderer = getCellRenderer(row, column);
2117 ICellStyle cellStyle = _tvs.getCellStyle(row, column);
2118 int ph = renderer.getPreferredHeight(gc, cellStyle, _tvs.getColumnWidth(column), row, column);
2119 if (ph > result) {
2120 result = ph;
2121 }
2122 }
2123 }
2124 if (result < _tvs.getMinimalRowHeight()) {
2125 result = _tvs.getMinimalRowHeight();
2126 }
2127 return result;
2128 }
2129
2130 /*** list of rows that will be optimized before the next drawing using the gc at hand. */
2131 protected Collection<IRow> _rowsToOptimize = Collections.synchronizedCollection(new HashSet<IRow>());
2132
2133 /***
2134 * Register a row for height optimization in the next redrwa (redraw triggered by this method).
2135 *
2136 * @param row row to optimize height for
2137 */
2138 public void optimizeHeight(IRow row) {
2139 _rowsToOptimize.add(row);
2140 syncedRedraw();
2141 }
2142
2143 /***
2144 * Remove a row from the list to optimize.
2145 *
2146 * @param row row to remove from the list
2147 */
2148 private void doNotOptimizeHeight(IRow row) {
2149 _rowsToOptimize.remove(row);
2150 }
2151
2152 /***
2153 * Register a list of rows for heigt optimization.
2154 *
2155 * @param rows list of rows to optimize
2156 */
2157 public void optimizeHeight(List<IRow> rows) {
2158 _rowsToOptimize.addAll(rows);
2159 syncedRedraw();
2160 }
2161
2162 /***
2163 * Calculates and sets the row height for all rows waiting to be optimized.
2164 *
2165 * @param gc GC for calculation of the heights
2166 */
2167 private void doRowHeightOptimization(GC gc) {
2168 for (IRow row : _rowsToOptimize) {
2169 int h = getPreferredRowHeight(gc, row);
2170 if (h != -1) {
2171 _tvs.setRowHeight(row, h);
2172 }
2173 }
2174 _rowsToOptimize.clear();
2175 }
2176
2177 /***
2178 * Scroll the addressed cell so, that is it completely visible.
2179 *
2180 * @param row row of the cell
2181 * @param column column of the cell
2182 */
2183 public void scrollToVisible(IRow row, IColumn column) {
2184
2185 int rIdx = _rows.indexOf(row);
2186 int cellY = getAbsBeginYForRowIdx(rIdx) - getFixedRowsHeight();
2187 int shownY = getAbsBeginYForRowIdx(_firstRowIdx) + _firstRowPixelOffset - getFixedRowsHeight();
2188 if (cellY < shownY) {
2189 if (getVerticalBar() != null) {
2190 getVerticalBar().setSelection(cellY);
2191 }
2192 } else {
2193 int cellHeight = _tvs.getRowHeight(row);
2194 if (getVerticalBar() != null) {
2195 getVerticalBar().setSelection(cellY + cellHeight - _tableRect.height);
2196 }
2197 }
2198
2199
2200 int cIdx = _cols.indexOf(column);
2201 int cellX = getAbsBeginXForColIdx(cIdx) - getFixedColumnsWidth();
2202 int shownX = getAbsBeginXForColIdx(_firstColIdx) - getFixedColumnsWidth();
2203 if (cellX < shownX) {
2204 if (getHorizontalBar() != null) {
2205 getHorizontalBar().setSelection(cellX);
2206 }
2207 } else {
2208 int cellWidth = _tvs.getColumnWidth(column);
2209 if (getHorizontalBar() != null) {
2210 getHorizontalBar().setSelection(cellX + cellWidth - _tableRect.width);
2211 }
2212 }
2213 updateScrollBars();
2214 redraw();
2215 }
2216
2217 /***
2218 * Return true, if the adressed cell is completely (i.e. not clipped) visible.
2219 *
2220 * @param row row of the cell
2221 * @param column column of the cell
2222 * @return true if the cell is completely visible
2223 */
2224 private boolean isCompleteVisible(IRow row, IColumn column) {
2225 RowInfo rInfo = getRowInfo(row);
2226 if (rInfo == null) {
2227 return false;
2228 }
2229 ColInfo cInfo = getColInfo(column);
2230 if (cInfo == null) {
2231 return false;
2232 }
2233 Rectangle b = getCellBounds(rInfo, cInfo);
2234 if (!(_tableRect.contains(b.x, b.y) && _tableRect.contains(b.x + b.width, b.y + b.height))) {
2235 if (_fixedColumns == 0 && _fixedRows == 0) {
2236 return false;
2237 } else {
2238
2239 if (_fixedColumns > 0 && _fixedColRect.contains(b.x, b.y)
2240 && _fixedColRect.contains(b.x + b.width, b.y + b.height)) {
2241 return true;
2242 }
2243 if (_fixedRows > 0 && _fixedRowRect.contains(b.x, b.y)
2244 && _fixedRowRect.contains(b.x + b.width, b.y + b.height)) {
2245 return true;
2246 }
2247 return false;
2248 }
2249 }
2250 return true;
2251 }
2252
2253 /***
2254 * Check whether a row is currently displayed.
2255 *
2256 * @param row row to check
2257 * @return true if the row is displayed.
2258 */
2259 public boolean isDisplayed(IRow row) {
2260 RowInfo rInfo = getRowInfo(row);
2261 return rInfo != null;
2262 }
2263
2264 /***
2265 * Check whether a column is currently displayed.
2266 *
2267 * @param column column to check
2268 * @return true if the column is displayed.
2269 */
2270 public boolean isDisplayed(IColumn column) {
2271 ColInfo cInfo = getColInfo(column);
2272 return cInfo != null;
2273 }
2274
2275 /***
2276 * Retrieve row by y coordinate of the bottom border.
2277 *
2278 * @param y y coordinate
2279 * @return a row identified by the bottom delimiter or <code>null</code>
2280 */
2281 private IRow rowByBottomBorder(int y) {
2282 for (RowInfo info : getRowInfos()) {
2283 int by = info.y + info.height;
2284 if (Math.abs(by - y) <= SELDELTA) {
2285 return info.row;
2286 }
2287 }
2288 return null;
2289 }
2290
2291 /***
2292 * Retrieve the row info for a row.
2293 *
2294 * @param row row to get the info for
2295 * @return info or <code>null</code>
2296 */
2297 protected RowInfo getRowInfo(IRow row) {
2298 for (RowInfo info : getRowInfos()) {
2299 if (info.row.equals(row)) {
2300 return info;
2301 }
2302 }
2303 return null;
2304 }
2305
2306 /***
2307 * Retrieve a column by its right border.
2308 *
2309 * @param x x coordinate
2310 * @return the column or <code>null</code>
2311 */
2312 private IColumn colByRightBorder(int x) {
2313 if (_colInfoCache != null) {
2314 for (ColInfo info : _colInfoCache) {
2315 int bx = info.x + info.width;
2316 if (Math.abs(bx - x) <= SELDELTA) {
2317 return info.column;
2318 }
2319 }
2320 }
2321 return null;
2322 }
2323
2324 /***
2325 * Retrieve cached col info for a row.
2326 *
2327 * @param col column to get the info for
2328 * @return colInfo or <code>null</code>
2329 */
2330 private ColInfo getColInfo(IColumn col) {
2331 if (_colInfoCache != null) {
2332 for (ColInfo info : _colInfoCache) {
2333 if (info.column.equals(col)) {
2334 return info;
2335 }
2336 }
2337 }
2338 return null;
2339 }
2340
2341 /***
2342 * Get the bounding rectangle for a cell.
2343 *
2344 * @param row row of the cell
2345 * @param column column of the cell
2346 * @return the bounding rectangle or <code>null</code> if the cell is not visible
2347 */
2348 public Rectangle getCellBounds(IRow row, IColumn column) {
2349 RowInfo rInfo = getRowInfo(row);
2350 ColInfo cInfo = getColInfo(column);
2351 if (rInfo == null || cInfo == null) {
2352 return null;
2353 }
2354 return getCellBounds(rInfo, cInfo);
2355 }
2356
2357 /***
2358 * Get the bounding rectangle for a cell.
2359 *
2360 * @param rInfo row info for the row
2361 * @param cInfo column info for the row
2362 * @return the bounding rectangle
2363 */
2364 private Rectangle getCellBounds(RowInfo rInfo, ColInfo cInfo) {
2365 return new Rectangle(cInfo.x, rInfo.y, cInfo.width, rInfo.height);
2366 }
2367
2368 /***
2369 * Retrieve the bounds for a cell.
2370 *
2371 * @param cell cell
2372 * @return cell bounds or null if the cell is not displayed
2373 */
2374 private Rectangle getCellBounds(IJaretTableCell cell) {
2375 return getCellBounds(cell.getRow(), cell.getColumn());
2376 }
2377
2378 /***
2379 * Get the bounding rect for a row.
2380 *
2381 * @param row row
2382 * @return bounding rect or <code>null</code> if the row is not visible
2383 */
2384 private Rectangle getRowBounds(IRow row) {
2385 RowInfo rInfo = getRowInfo(row);
2386 return rInfo == null ? null : getRowBounds(rInfo);
2387 }
2388
2389 /***
2390 * Get bounding rect for a row by the rowinfo.
2391 *
2392 * @param info row info
2393 * @return bounding rect
2394 */
2395 private Rectangle getRowBounds(RowInfo info) {
2396 int bx = _fixedColumns == 0 ? _tableRect.x : _fixedColRect.x;
2397 int width = _fixedColumns == 0 ? _tableRect.width : _fixedColRect.width + _tableRect.width;
2398 return new Rectangle(bx, info.y, width, info.height);
2399 }
2400
2401 /***
2402 * Get the bounding rect for a column.
2403 *
2404 * @param column column
2405 * @return bounding rect or <code>null</code> if the column is not visible
2406 */
2407 public Rectangle getColumnBounds(IColumn column) {
2408 ColInfo cInfo = getColInfo(column);
2409
2410 return cInfo != null ? getColumnBounds(cInfo) : null;
2411 }
2412
2413 /***
2414 * Get the bounds for a column.
2415 *
2416 * @param info column info
2417 * @return bounding rectangle
2418 */
2419 private Rectangle getColumnBounds(ColInfo info) {
2420 int by = _fixedRows == 0 ? _tableRect.y : _fixedRowRect.y;
2421 int height = _fixedRows == 0 ? _tableRect.height : _fixedRowRect.height + _tableRect.height;
2422 return new Rectangle(info.x, by, info.width, height);
2423 }
2424
2425 /***
2426 * Redraw a cell. Method can be called from any thread.
2427 *
2428 * @param row row of the cell
2429 * @param column column of the cell
2430 */
2431 private void redraw(IRow row, IColumn column) {
2432 Rectangle r = getCellBounds(row, column);
2433 if (r != null) {
2434 syncedRedraw(r.x, r.y, r.width, r.height);
2435 }
2436 }
2437
2438 /***
2439 * Redraw a row. Method can be called from any thread.
2440 *
2441 * @param row row to be painted
2442 */
2443 private void redraw(IRow row) {
2444 Rectangle r = getRowBounds(row);
2445 if (r != null) {
2446 syncedRedraw(r.x, r.y, r.width, r.height);
2447 }
2448 }
2449
2450 /***
2451 * Redraw a column. Method can be called from any thread.
2452 *
2453 * @param column column to be repainted
2454 */
2455 private void redraw(IColumn column) {
2456 Rectangle r = getColumnBounds(column);
2457 if (r != null) {
2458 syncedRedraw(r.x, r.y, r.width, r.height);
2459 }
2460 }
2461
2462 /***
2463 * Redraw a region. This method can be called from any thread.
2464 *
2465 * @param x x coordinate
2466 * @param y y coordinate
2467 * @param width width of the region
2468 * @param height height of the region
2469 */
2470 private void syncedRedraw(final int x, final int y, final int width, final int height) {
2471 Runnable r = new Runnable() {
2472 public void run() {
2473 if (!isDisposed()) {
2474 redraw(x, y, width, height, true);
2475 }
2476 }
2477 };
2478 Display.getCurrent().syncExec(r);
2479 }
2480
2481 /***
2482 * Redraw complete area. Safe to call from any thread.
2483 *
2484 */
2485 private void syncedRedraw() {
2486 Runnable r = new Runnable() {
2487 public void run() {
2488 if (!isDisposed()) {
2489 redraw();
2490 }
2491 }
2492 };
2493 Display.getCurrent().syncExec(r);
2494 }
2495
2496
2497
2498 /***
2499 * Update the scrollbars.
2500 */
2501 protected void updateScrollBars() {
2502 updateYScrollBar();
2503 updateXScrollBar();
2504 }
2505
2506 /***
2507 * Update the horiontal scrollbar.
2508 */
2509 private void updateXScrollBar() {
2510 if (Display.getCurrent() != null) {
2511 Display.getCurrent().syncExec(new Runnable() {
2512 public void run() {
2513 ScrollBar scroll = getHorizontalBar();
2514
2515 if (scroll != null) {
2516 _oldHorizontalScroll = -1;
2517 scroll.setMinimum(0);
2518 scroll.setMaximum(getTotalWidth() - getFixedColumnsWidth());
2519 scroll.setThumb(getWidth() - getFixedColumnsWidth());
2520 scroll.setIncrement(50);
2521 scroll.setPageIncrement(getWidth());
2522 }
2523 }
2524 });
2525 }
2526 }
2527
2528 /*** last horizontal scroll value for scroll optimization. */
2529 private int _oldHorizontalScroll = -1;
2530 /*** last vertical scroll value for scroll optimization. */
2531 private int _oldVerticalScroll = -1;
2532
2533 /*** if true use optimized scrolling. */
2534 private boolean _optimizeScrolling = true;
2535
2536 /***
2537 * Handle a change of the horizontal scrollbar.
2538 *
2539 * @param event SelectionEvent
2540 */
2541 private void handleHorizontalScroll(SelectionEvent event) {
2542 int value = getHorizontalBar().getSelection() + getFixedColumnsWidth();
2543 int colIdx = getColIdxForAbsX(value);
2544 int offset = value - getAbsBeginXForColIdx(colIdx);
2545 int diff = _oldHorizontalScroll - value;
2546
2547 if (Math.abs(diff) > _tableRect.width / 2 || _oldHorizontalScroll == -1 || !_optimizeScrolling) {
2548 _firstColIdx = colIdx;
2549 _firstColPixelOffset = offset;
2550 redraw();
2551 } else {
2552 if (diff > 0) {
2553 scroll(_tableRect.x + diff, 0, _tableRect.x, 0, _tableRect.width - diff, getHeight(), false);
2554 } else {
2555 diff = -diff;
2556 scroll(_tableRect.x, 0, _tableRect.x + diff, 0, _tableRect.width - diff, getHeight(), false);
2557 }
2558 _firstColIdx = colIdx;
2559 _firstColPixelOffset = offset;
2560 }
2561 _oldHorizontalScroll = value;
2562 }
2563
2564 /***
2565 * Update the vertical scrollbar if present.
2566 */
2567 public void updateYScrollBar() {
2568 if (Display.getCurrent() != null) {
2569 Display.getCurrent().syncExec(new Runnable() {
2570 public void run() {
2571 ScrollBar scroll = getVerticalBar();
2572
2573 if (scroll != null) {
2574 _oldVerticalScroll = -1;
2575 scroll.setMinimum(0);
2576 scroll.setMaximum(getTotalHeight() - getFixedRowsHeight());
2577 int height = getHeight();
2578 if (_tableRect != null) {
2579 height = _tableRect.height;
2580 }
2581 scroll.setThumb(height);
2582 scroll.setIncrement(50);
2583 scroll.setPageIncrement(getHeight());
2584 scroll.setSelection(getAbsBeginYForRowIdx(_firstRowIdx) + _firstRowPixelOffset
2585 + getFixedRowsHeight());
2586 }
2587 }
2588 });
2589 }
2590 }
2591
2592 /***
2593 * Handle a selection on the vertical scroll bar (a vertical scroll).
2594 *
2595 * @param event selection
2596 */
2597 private void handleVerticalScroll(SelectionEvent event) {
2598 int value = getVerticalBar().getSelection() + getFixedRowsHeight();
2599 int rowidx = getRowIdxForAbsY(value);
2600 int offset = value - getAbsBeginYForRowIdx(rowidx);
2601 int oldFirstIdx = _firstRowIdx;
2602 int oldPixelOffset = _firstRowPixelOffset;
2603
2604 int diff = _oldVerticalScroll - value;
2605
2606 if (Math.abs(diff) > _tableRect.height / 2 || _oldVerticalScroll == -1 || !_optimizeScrolling) {
2607 update();
2608 _firstRowIdx = rowidx;
2609 _firstRowPixelOffset = offset;
2610 _rowInfoCache = null;
2611 redraw();
2612 } else {
2613 Rectangle completeArea = new Rectangle(_fixedColRect.x, _tableRect.y, _fixedColRect.width
2614 + _tableRect.width, _tableRect.height);
2615
2616 if (diff > 0) {
2617 scroll(0, completeArea.y + diff, 0, completeArea.y, getWidth(), completeArea.height - diff, false);
2618 } else {
2619 diff = -diff;
2620 scroll(0, completeArea.y, 0, completeArea.y + diff, getWidth(), completeArea.height - diff, false);
2621 }
2622 _firstRowIdx = rowidx;
2623 _firstRowPixelOffset = offset;
2624 _rowInfoCache = null;
2625 }
2626 _oldVerticalScroll = value;
2627 firePropertyChange(PROPERTYNAME_FIRSTROWIDX, oldFirstIdx, _firstRowIdx);
2628 firePropertyChange(PROPERTYNAME_FIRSTROWPIXELOFFSET, oldPixelOffset, _firstRowPixelOffset);
2629 }
2630
2631 /***
2632 * Get the absolute begin x for a column.
2633 *
2634 * @param colIdx index of the column (in the displayed columns)
2635 * @return the absolute x coordinate
2636 */
2637 public int getAbsBeginXForColIdx(int colIdx) {
2638 int x = 0;
2639 for (int idx = 0; idx < colIdx; idx++) {
2640 IColumn col = _cols.get(idx);
2641 int colWidth = _tvs.getColumnWidth(col);
2642 x += colWidth;
2643 }
2644 return x;
2645 }
2646
2647 /***
2648 * Get the absolute begin x for a column.
2649 *
2650 * @param column the column
2651 * @return the absolute x coordinate
2652 */
2653 public int getAbsBeginXForColumn(IColumn column) {
2654 return getAbsBeginXForColIdx(_cols.indexOf(column));
2655 }
2656
2657 /***
2658 * Retrieve the beginning x coordinate for a column.
2659 *
2660 * @param column column
2661 * @return beginning x coordinate for drawing that column
2662 */
2663 private int xForCol(IColumn column) {
2664 int x = getAbsBeginXForColIdx(_cols.indexOf(column));
2665 int begin = getAbsBeginXForColIdx(_firstColIdx) + _firstColPixelOffset;
2666 return x - begin + _fixedColRect.width;
2667 }
2668
2669 /***
2670 * Return the (internal) index of the column corresponding to the given x coordinate value (absolute value taking
2671 * all visible columns into account).
2672 *
2673 * @param absX absolute x coordinate
2674 * @return the column index in the internal list of columns (or -1 if none could be determined)
2675 */
2676 public int getColIdxForAbsX(int absX) {
2677 int idx = 0;
2678 int x = 0;
2679 while (x <= absX && idx < _cols.size()) {
2680 IColumn col = _cols.get(idx);
2681 int colWidth = _tvs.getColumnWidth(col);
2682 x += colWidth;
2683 idx++;
2684 }
2685 return idx < _cols.size() ? idx - 1 : -1;
2686 }
2687
2688 /***
2689 * Return the column for an absolute x coordinate.
2690 *
2691 * @param absX absolute x coordinate
2692 * @return the column for the coordinate
2693 */
2694 public IColumn getColumnForAbsX(int absX) {
2695 return _cols.get(getColIdxForAbsX(absX));
2696 }
2697
2698 /***
2699 * Return the absolute y coordinate for the given row (given by index).
2700 *
2701 * @param rowidx index of the row
2702 * @return absolute y coordinate (of all rows) for the row
2703 */
2704 private int getAbsBeginYForRowIdx(int rowidx) {
2705 int y = 0;
2706 for (int idx = 0; idx < rowidx; idx++) {
2707 IRow row = _rows.get(idx);
2708 int rowHeight = _tvs.getRowHeight(row);
2709 y += rowHeight;
2710 }
2711 return y;
2712 }
2713
2714 /***
2715 * Get row index for an absolute y coordinate (thought on the full height table with all rows).
2716 *
2717 * @TODO optimize
2718 * @param absY absolute y position (thought on the full table height)
2719 * @return index of the corresponding row
2720 */
2721 public int getRowIdxForAbsY(int absY) {
2722 int idx = 0;
2723 int y = 0;
2724 while (y <= absY) {
2725 IRow row = _rows.get(idx);
2726 int rowHeight = _tvs.getRowHeight(row);
2727 y += rowHeight;
2728 idx++;
2729 }
2730 return idx - 1;
2731 }
2732
2733 /***
2734 * Retrie internal index of gicen row.
2735 *
2736 * @param row row
2737 * @return internal index or -1 if the row is not in the internal list
2738 */
2739 private int getRowIdx(IRow row) {
2740 return row != null ? _rows.indexOf(row) : -1;
2741 }
2742
2743 /***
2744 * Retrieve the row corresponding to a specified y coordinate.
2745 *
2746 * @param y y
2747 * @return row for that y ycoordinate or <code>null</code> if no row could be determined.
2748 */
2749 public IRow rowForY(int y) {
2750 if ((y < _tableRect.y || y > _tableRect.y + _tableRect.height) && _fixedRows == 0) {
2751 return null;
2752 }
2753
2754 for (RowInfo rInfo : getRowInfos()) {
2755 if (y >= rInfo.y && y < rInfo.y + rInfo.height) {
2756 return rInfo.row;
2757 }
2758 }
2759 return null;
2760 }
2761
2762 /***
2763 * Retrive the list of currently availbale RowInfo etries.
2764 *
2765 * @return list of rowinfos
2766 */
2767 private List<RowInfo> getRowInfos() {
2768 if (_rowInfoCache == null) {
2769 fillRowInfoCache();
2770 }
2771 return _rowInfoCache;
2772 }
2773
2774 /***
2775 * Fill the cache of row infos for the current viewconfiguration.
2776 */
2777 private void fillRowInfoCache() {
2778 if (_rowInfoCache != null) {
2779 return;
2780 }
2781 _rowInfoCache = new ArrayList<RowInfo>();
2782
2783
2784 int y = 0;
2785 if (_fixedRows > 0) {
2786 y = _fixedRowRect.y;
2787 for (int rIdx = 0; rIdx < _fixedRows; rIdx++) {
2788 IRow row = _rows.get(rIdx);
2789 int rHeight = _tvs.getRowHeight(row);
2790 _rowInfoCache.add(new RowInfo(row, y, rHeight, true));
2791 y += rHeight;
2792 }
2793
2794 }
2795
2796 int rIdx = _firstRowIdx;
2797 y = _tableRect.y - _firstRowPixelOffset;
2798
2799 while (y < getHeight() && rIdx < _rows.size()) {
2800 IRow row = _rows.get(rIdx);
2801 int rHeight = _tvs.getRowHeight(row);
2802 _rowInfoCache.add(new RowInfo(row, y, rHeight, false));
2803 y += rHeight;
2804 rIdx++;
2805 }
2806 }
2807
2808 /***
2809 * Retrieve a row from the internal list of rows.
2810 *
2811 * @param idx index in the internal list
2812 * @return the row for idx
2813 */
2814 private IRow rowForIdx(int idx) {
2815 return _rows.get(idx);
2816 }
2817
2818 /***
2819 * Retrieve the column corresponding to a x coordinate.
2820 *
2821 * @param x x
2822 * @return the corresponding column or <code>null</code> if none could be determined
2823 */
2824 public IColumn colForX(int x) {
2825 if ((x < _tableRect.x || x > _tableRect.x + _tableRect.width) && _fixedColumns == 0) {
2826 return null;
2827 }
2828 for (ColInfo cInfo : _colInfoCache) {
2829 if (x >= cInfo.x && x < cInfo.x + cInfo.width) {
2830 return cInfo.column;
2831 }
2832 }
2833 return null;
2834 }
2835
2836 /***
2837 * Get the column for a given index.
2838 *
2839 * @param idx index
2840 * @return tghe column
2841 */
2842 private IColumn colForIdx(int idx) {
2843 return _cols.get(idx);
2844 }
2845
2846 /***
2847 * Get the index of a given column.
2848 *
2849 * @param column column
2850 * @return the index of the column or -1 if no index could be given
2851 */
2852 private int getColumnIdx(IColumn column) {
2853 return column != null ? _cols.indexOf(column) : -1;
2854 }
2855
2856 /***
2857 * Retrieve TableXCell for given pixel coordinates.
2858 *
2859 * @param x pixel coordinate x
2860 * @param y pixel coordinate y
2861 * @return table cel if found or <code>null</code> if no cell can be found
2862 */
2863 public IJaretTableCell getCell(int x, int y) {
2864 if (_tableRect.contains(x, y)) {
2865 IRow row = rowForY(y);
2866 IColumn col = colForX(x);
2867 if (row == null || col == null) {
2868 return null;
2869 }
2870 return new JaretTableCellImpl(row, col);
2871 }
2872 return null;
2873 }
2874
2875 /***
2876 * Retrieve a table cell for given index coordinates.
2877 *
2878 * @param colIdx column index (X)
2879 * @param rowIdx row index (Y)
2880 * @return table cell
2881 */
2882 public IJaretTableCell getCellForIdx(int colIdx, int rowIdx) {
2883 return new JaretTableCellImpl(rowForIdx(rowIdx), colForIdx(colIdx));
2884 }
2885
2886 /***
2887 * Set a table model to be displayed by the jaret table.
2888 *
2889 * @param model the table model to be displayed.
2890 */
2891 public void setTableModel(IJaretTableModel model) {
2892 if (_model != null) {
2893 _model.removeJaretTableModelListener(this);
2894 }
2895 _model = model;
2896 _model.addJaretTableModelListener(this);
2897
2898 _hierarchicalModel = null;
2899
2900
2901 List<IColumn> cList = new ArrayList<IColumn>();
2902 for (int i = 0; i < model.getColumnCount(); i++) {
2903 cList.add(model.getColumn(i));
2904 }
2905 _tvs.setSortedColumns(cList);
2906
2907 updateColumnList();
2908 registerRowsForOptimization();
2909 updateRowList();
2910
2911 updateYScrollBar();
2912 updateXScrollBar();
2913 redraw();
2914 }
2915
2916 /***
2917 * Set a hierarchical table model. This will internally create a StdHierrahicalTableModel that is a normal
2918 * TbaleModel including only the expanded rows.
2919 *
2920 * @param hmodel hierarchical model to display
2921 */
2922 public void setTableModel(IHierarchicalJaretTableModel hmodel) {
2923 if (_model != null) {
2924 _model.removeJaretTableModelListener(this);
2925 }
2926 if (_tvs != null) {
2927 _tvs.removeTableViewStateListener(this);
2928 }
2929 _tvs = new DefaultHierarchicalTableViewState();
2930 _tvs.addTableViewStateListener(this);
2931 _model = new StdHierarchicalTableModel(hmodel, (IHierarchicalTableViewState) _tvs);
2932 _model.addJaretTableModelListener(this);
2933
2934 _hierarchicalModel = hmodel;
2935
2936 updateColumnList();
2937 registerRowsForOptimization();
2938 updateRowList();
2939 updateColumnList();
2940 updateYScrollBar();
2941 updateXScrollBar();
2942 redraw();
2943 }
2944
2945 /***
2946 * Retrieve a hierarchical model if set.
2947 *
2948 * @return hierarchical model or <code>null</code>
2949 */
2950 public IHierarchicalJaretTableModel getHierarchicalModel() {
2951 return _hierarchicalModel;
2952 }
2953
2954 /***
2955 * Retrieve the displayed table model.
2956 *
2957 * @return the table model
2958 */
2959 public IJaretTableModel getTableModel() {
2960 return _model;
2961 }
2962
2963 /***
2964 * Add a column to the underlying table model. Model has to be set for that operation or an IllegalStateException
2965 * will be thrown.
2966 *
2967 * @param column column to be added
2968 */
2969 public void addColumn(IColumn column) {
2970 if (_model != null) {
2971 _model.addColumn(column);
2972 } else {
2973 throw new IllegalStateException("model has to be set for the operation.");
2974 }
2975 }
2976
2977 /***
2978 * Registers all rows in the model for optimization that have a mode indicating optimal height.
2979 *
2980 */
2981 private void registerRowsForOptimization() {
2982 if (_model != null) {
2983 for (int i = 0; i < _model.getRowCount(); i++) {
2984 IRow row = _model.getRow(i);
2985 if (_tvs.getRowHeigthMode(row) == ITableViewState.RowHeightMode.OPTANDVAR
2986 || _tvs.getRowHeigthMode(row) == ITableViewState.RowHeightMode.OPTIMAL) {
2987 optimizeHeight(row);
2988 }
2989 }
2990 }
2991 }
2992
2993 /***
2994 * Update the internal rowlist according to filter and sorter.
2995 *
2996 */
2997 private void updateRowList() {
2998 _rows = new ArrayList<IRow>();
2999 if (_model != null) {
3000 for (int i = 0; i < _model.getRowCount(); i++) {
3001 IRow row = _model.getRow(i);
3002 if (i < _fixedRows) {
3003
3004 _rows.add(row);
3005 } else if (_rowFilter == null || (_rowFilter != null && _rowFilter.isInResult(row))) {
3006 if (!_autoFilterEnabled || _autoFilter.isInResult(row)) {
3007 if (!_rows.contains(row)) {
3008 _rows.add(row);
3009 }
3010 }
3011 }
3012 }
3013 }
3014
3015
3016 RowComparator comparator = new RowComparator();
3017 IRowSorter rs = null;
3018 if (comparator.canSort()) {
3019 rs = comparator;
3020 } else {
3021 rs = _rowSorter;
3022 }
3023 if (rs != null) {
3024 List<IRow> fixedRows = new ArrayList<IRow>();
3025
3026 if (_excludeFixedRowsFromSorting) {
3027 for (int i = 0; i < _fixedRows; i++) {
3028 fixedRows.add(_rows.remove(0));
3029 }
3030 }
3031 Collections.sort(_rows, rs);
3032 if (_excludeFixedRowsFromSorting) {
3033 for (int i = fixedRows.size() - 1; i >= 0; i--) {
3034 _rows.add(0, fixedRows.get(i));
3035 }
3036 }
3037 }
3038
3039 updateYScrollBar();
3040 }
3041
3042 /***
3043 * Get the index of the given row in the internal, fileterd list of rows.
3044 *
3045 * @param row row to retrieve the index for
3046 * @return index of the row or -1 if the row is not in filtered list of rows
3047 */
3048 public int getInternalRowIndex(IRow row) {
3049 return _rows.indexOf(row);
3050 }
3051
3052 /***
3053 * Comparator based on the sorting settings of the columns.
3054 *
3055 * @author Peter Kliem
3056 * @version $Id: JaretTable.java 1076 2010-12-05 13:34:42Z kliem $
3057 */
3058 public class RowComparator extends PropertyObservableBase implements IRowSorter {
3059 /*** arary of Row comparators (IColumns are Comparators for rows!). */
3060 private List<Comparator<IRow>> _comparators = new ArrayList<Comparator<IRow>>();
3061
3062 /***
3063 * Construct it. Initializes itself with the columns.
3064 */
3065 public RowComparator() {
3066 IColumn[] arr = new IColumn[_cols.size()];
3067 int max = 0;
3068 for (IColumn col : _cols) {
3069 int sortP = _tvs.getColumnSortingPosition(col);
3070 if (sortP > 0) {
3071 arr[sortP] = col;
3072 if (sortP > max) {
3073 max = sortP;
3074 }
3075 }
3076 }
3077 for (int i = 1; i <= max; i++) {
3078 _comparators.add(arr[i]);
3079 }
3080
3081 }
3082
3083 /***
3084 * Check whether the comparator is able to sort.
3085 *
3086 * @return true if comparators are present
3087 */
3088 public boolean canSort() {
3089 return _comparators.size() > 0;
3090 }
3091
3092 /***
3093 * {@inheritDoc}
3094 */
3095 public int compare(IRow r1, IRow r2) {
3096 for (Comparator<IRow> comp : _comparators) {
3097 int res = comp.compare(r1, r2);
3098 res = _tvs.getColumnSortingDirection((IColumn) comp) ? res : -res;
3099 if (res != 0) {
3100 return res;
3101 }
3102 }
3103 return 0;
3104 }
3105
3106 }
3107
3108 /***
3109 * Update the internal column list. Should be called whenever a column changes visibility or the column order has
3110 * been changed.
3111 *
3112 */
3113 public void updateColumnList() {
3114 _cols = new ArrayList<IColumn>();
3115
3116 for (int i = 0; i < _model.getColumnCount(); i++) {
3117 if (i < _tvs.getSortedColumns().size()) {
3118 IColumn col = _tvs.getSortedColumns().get(i);
3119 if (_tvs.getColumnVisible(col)) {
3120 _cols.add(col);
3121 }
3122 }
3123 }
3124
3125 for (int i = 0; i < _model.getColumnCount(); i++) {
3126 IColumn col = _model.getColumn(i);
3127 if (!_cols.contains(col) && _tvs.getColumnVisible(col)) {
3128 _cols.add(col);
3129 }
3130 }
3131 }
3132
3133 /***
3134 * Handling of the paint event -> do the painting.
3135 *
3136 * @param event PaintEvent
3137 */
3138 private void onPaint(PaintEvent event) {
3139 if (event.width == 0 || event.height == 0) {
3140 return;
3141 }
3142
3143 long time = System.currentTimeMillis();
3144
3145 _rowInfoCache = null;
3146 GC gc = event.gc;
3147
3148
3149 doRowHeightOptimization(gc);
3150
3151
3152 paint(gc, getWidth(), getHeight());
3153 if (DEBUGPAINTTIME) {
3154 System.out.println("time " + (System.currentTimeMillis() - time) + " ms");
3155 }
3156 }
3157
3158 /***
3159 * Calculate the layout of the table area rectangles.
3160 *
3161 * @param width width of the table
3162 * @param height height of the table
3163 */
3164 private void preparePaint(int width, int height) {
3165 if (_drawHeader) {
3166 _headerRect = new Rectangle(0, 0, width, _headerHeight);
3167 } else {
3168 _headerRect = new Rectangle(0, 0, 0, 0);
3169 }
3170
3171 if (_autoFilterEnabled) {
3172
3173 int autoFilterHeight = getPreferredAutoFilterHeight();
3174 _autoFilterRect = new Rectangle(0, _headerRect.y + _headerRect.height, _headerRect.width, autoFilterHeight);
3175 _tableRect = new Rectangle(0, _autoFilterRect.y + _autoFilterRect.height, width, height
3176 - _autoFilterRect.height);
3177 } else {
3178 _tableRect = new Rectangle(0, _headerRect.y + _headerRect.height, width, height - _headerRect.height);
3179 }
3180
3181
3182 if (_fixedColumns > 0) {
3183 int fWidth = getFixedColumnsWidth();
3184 _headerRect.x = _headerRect.x + fWidth;
3185 _headerRect.width = _headerRect.width - fWidth;
3186
3187 if (_autoFilterEnabled) {
3188 _autoFilterRect.x = _headerRect.x;
3189 _autoFilterRect.width = _headerRect.width;
3190 }
3191
3192 _tableRect.x = _headerRect.x;
3193 _tableRect.width = _headerRect.width;
3194
3195 _fixedColRect = new Rectangle(0, _tableRect.y, fWidth, _tableRect.height);
3196
3197 } else {
3198 _fixedColRect = new Rectangle(_tableRect.x, _tableRect.y, 0, _tableRect.height);
3199 }
3200
3201
3202 if (_fixedRows > 0) {
3203 int fHeight = getFixedRowsHeight();
3204 if (_autoFilterEnabled) {
3205 _fixedRowRect = new Rectangle(0, _autoFilterRect.y + _autoFilterRect.height, width,
3206 getFixedRowsHeight());
3207 } else {
3208 _fixedRowRect = new Rectangle(0, _headerRect.y + _headerRect.height, width, getFixedRowsHeight());
3209 }
3210
3211 _tableRect.y = _tableRect.y + fHeight;
3212 _tableRect.height = _tableRect.height - fHeight;
3213 if (_fixedColumns > 0) {
3214 _fixedColRect.y = _tableRect.y;
3215 _fixedColRect.height = _tableRect.height;
3216 }
3217 } else {
3218
3219 _fixedRowRect = new Rectangle(_tableRect.x, _tableRect.y, _tableRect.width, 0);
3220 }
3221
3222 }
3223
3224 /***
3225 * Retrieve the maximum preferred height of the autofilter controls.
3226 *
3227 * @return preferred height for the autofilters
3228 */
3229 private int getPreferredAutoFilterHeight() {
3230 int result = 0;
3231 for (IAutoFilter af : _autoFilterMap.values()) {
3232 int height = af.getControl().computeSize(SWT.DEFAULT, SWT.DEFAULT).y;
3233 if (height > result) {
3234 result = height;
3235 }
3236 }
3237 return result;
3238 }
3239
3240 /***
3241 * The main paint method.
3242 *
3243 * @param gc GC
3244 * @param width width of the control
3245 * @param height height of the control
3246 */
3247 public void paint(GC gc, int width, int height) {
3248 preparePaint(width, height);
3249
3250
3251 Color bg = gc.getBackground();
3252 gc.setBackground(getBackground());
3253 gc.fillRectangle(gc.getClipping());
3254 gc.setBackground(bg);
3255
3256 drawHeader(gc, width, height);
3257 drawTableArea(gc, width, height);
3258
3259 setUpAutoFilter(gc);
3260
3261
3262 if (_isFillDrag) {
3263 drawFillDragBorder(gc);
3264 }
3265
3266 }
3267
3268 /***
3269 * Setup the autofilter components.
3270 *
3271 * @param gc GC
3272 */
3273 private void setUpAutoFilter(GC gc) {
3274 if (_autoFilterEnabled) {
3275 for (IColumn column : _cols) {
3276 IAutoFilter af = _autoFilterMap.get(column);
3277 ColInfo cInfo = getColInfo(column);
3278 if (af != null && cInfo == null) {
3279 af.getControl().setVisible(false);
3280 } else {
3281 if (af != null) {
3282 af.getControl().setVisible(true);
3283 af.getControl().setBounds(cInfo.x, _autoFilterRect.y, cInfo.width, _autoFilterRect.height);
3284 }
3285 }
3286 }
3287 } else {
3288 for (IColumn column : _cols) {
3289 IAutoFilter af = _autoFilterMap.get(column);
3290 if (af != null) {
3291 af.getControl().setVisible(false);
3292 }
3293 }
3294 }
3295 }
3296
3297 /***
3298 * Draw the table header.
3299 *
3300 * @param gc gc
3301 * @param width width of the table
3302 * @param height height of the table
3303 */
3304 private void drawHeader(GC gc, int width, int height) {
3305 if (_headerRenderer != null && _drawHeader) {
3306
3307 for (int cIdx = 0; cIdx < _fixedColumns; cIdx++) {
3308 int x = getAbsBeginXForColIdx(cIdx);
3309 IColumn col = _cols.get(cIdx);
3310 int colwidth = _tvs.getColumnWidth(col);
3311
3312 if (!_headerRenderer.disableClipping()) {
3313 gc.setClipping(x, _headerRect.y, colwidth, _headerRect.height);
3314 }
3315
3316 if (col.displayHeader()) {
3317 drawHeader(gc, x, colwidth, col);
3318 }
3319 }
3320
3321
3322 int x = -_firstColPixelOffset;
3323 x += _tableRect.x;
3324 int cIdx = _firstColIdx;
3325 while (x < getWidth() && cIdx < _cols.size()) {
3326 IColumn col = _cols.get(cIdx);
3327 int colwidth = _tvs.getColumnWidth(col);
3328 int xx = x > _headerRect.x ? x : _headerRect.x;
3329 int clipWidth = x > _headerRect.x ? colwidth : colwidth - _firstColPixelOffset;
3330 if (!_headerRenderer.disableClipping()) {
3331 gc.setClipping(xx, _headerRect.y, clipWidth, _headerRect.height);
3332 } else if (_fixedColumns > 0) {
3333
3334
3335 gc.setClipping(xx, _headerRect.y, _tableRect.width - xx, _headerRect.height);
3336 }
3337
3338
3339 if (col.displayHeader()) {
3340 drawHeader(gc, x, colwidth, col);
3341 }
3342
3343 x += colwidth;
3344 cIdx++;
3345 }
3346 }
3347
3348 }
3349
3350 /***
3351 * Render the header for one column.
3352 *
3353 * @param gc gc
3354 * @param x starting x
3355 * @param colwidth width
3356 * @param col column
3357 */
3358 private void drawHeader(GC gc, int x, int colwidth, IColumn col) {
3359 Rectangle area = new Rectangle(x, _headerRect.y, colwidth, _headerRect.height);
3360 int sortingPos = _tvs.getColumnSortingPosition(col);
3361 boolean sortingDir = _tvs.getColumnSortingDirection(col);
3362 _headerRenderer.draw(gc, area, col, sortingPos, sortingDir, false);
3363 }
3364
3365 /***
3366 * Convenience method to check whether a certain cell is selected.
3367 *
3368 * @param row row of the cell
3369 * @param column column of the cell
3370 * @return true if the cell is selected (by itself, a row of a column selection)
3371 */
3372 public boolean isSelected(IRow row, IColumn column) {
3373 if (_selectionModel.getSelection().getSelectedRows().contains(row)) {
3374 return true;
3375 }
3376 if (_selectionModel.getSelection().getSelectedColumns().contains(column)) {
3377 return true;
3378 }
3379 IJaretTableCell cell = new JaretTableCellImpl(row, column);
3380 if (_selectionModel.getSelection().getSelectedCells().contains(cell)) {
3381 return true;
3382 }
3383 return false;
3384 }
3385
3386 /***
3387 * Draw the main table area including fixed rows and columns.
3388 *
3389 * @param gc gc
3390 * @param width width of the area
3391 * @param height height of the area
3392 */
3393 private void drawTableArea(GC gc, int width, int height) {
3394 _colInfoCache.clear();
3395 boolean colCacheFilled = false;
3396
3397 Rectangle clipSave = gc.getClipping();
3398
3399
3400 for (RowInfo rowInfo : getRowInfos()) {
3401 int y = rowInfo.y;
3402 IRow row = rowInfo.row;
3403 int rHeight = _tvs.getRowHeight(row);
3404
3405 int yclip = y;
3406 if (rowInfo.fixed && yclip < _fixedRowRect.y) {
3407 yclip = _fixedRowRect.y;
3408 } else if (!rowInfo.fixed && yclip < _tableRect.y) {
3409 yclip = _tableRect.y;
3410 }
3411
3412 int x = 0;
3413
3414 if (_fixedColumns > 0) {
3415 x = _fixedColRect.x;
3416 for (int cIdx = 0; cIdx < _fixedColumns; cIdx++) {
3417 IColumn col = _cols.get(cIdx);
3418 int colwidth = _tvs.getColumnWidth(col);
3419 if (!colCacheFilled) {
3420 _colInfoCache.add(new ColInfo(col, x, colwidth));
3421 }
3422
3423 gc.setClipping(x, yclip, colwidth + 1, rHeight + 1);
3424 Rectangle area = new Rectangle(x, y, colwidth, rHeight);
3425
3426 drawCell(gc, area, row, col);
3427
3428 x += colwidth;
3429 }
3430 }
3431
3432
3433 x = _tableRect.x - _firstColPixelOffset;
3434 int cIdx = _firstColIdx;
3435 while (x < getWidth() && cIdx < _cols.size()) {
3436 IColumn col = _cols.get(cIdx);
3437 int colwidth = _tvs.getColumnWidth(col);
3438 if (!colCacheFilled) {
3439 _colInfoCache.add(new ColInfo(col, x, colwidth));
3440 }
3441 int xx = x > _tableRect.x ? x : _tableRect.x;
3442 int clipWidth = x > _tableRect.x ? colwidth : colwidth - _firstColPixelOffset;
3443
3444 gc.setClipping(xx, yclip, clipWidth + 1, rHeight + 1);
3445 Rectangle area = new Rectangle(x, y, colwidth, rHeight);
3446
3447 drawCell(gc, area, row, col);
3448
3449 x += colwidth;
3450 cIdx++;
3451 }
3452 colCacheFilled = true;
3453 }
3454
3455
3456
3457 if (!colCacheFilled) {
3458
3459 int x = 0;
3460 if (_fixedColumns > 0) {
3461 for (int i = 0; i < _fixedColumns; i++) {
3462 IColumn col = _cols.get(i);
3463 int colwidth = _tvs.getColumnWidth(col);
3464 _colInfoCache.add(new ColInfo(col, x, colwidth));
3465 x += colwidth;
3466 }
3467 }
3468
3469 x = -_firstColPixelOffset;
3470 x += _tableRect.x;
3471 int cIdx = _firstColIdx;
3472 while (x < getWidth() && cIdx < _cols.size()) {
3473 IColumn col = _cols.get(cIdx);
3474 int colwidth = _tvs.getColumnWidth(col);
3475 _colInfoCache.add(new ColInfo(col, x, colwidth));
3476 x += colwidth;
3477 cIdx++;
3478 }
3479
3480 }
3481
3482
3483 if (_fixedColRect != null && _fixedColumns > 0) {
3484 int maxY = _fixedColRect.y + _fixedColRect.height - 1;
3485 if (_rows != null && _rows.size() > 0) {
3486 IRow lastRow = _rows.get(_rows.size() - 1);
3487 Rectangle bounds = getRowBounds(lastRow);
3488 if (bounds != null) {
3489 maxY = bounds.y + bounds.height;
3490 }
3491 }
3492 gc.setClipping(new Rectangle(0, 0, width, height));
3493 int fx = _fixedColRect.x + _fixedColRect.width - 1;
3494 gc.drawLine(fx, _fixedRowRect.y, fx, maxY);
3495 gc.setClipping(clipSave);
3496 }
3497
3498 if (_fixedRowRect != null && _fixedRows > 0) {
3499 int maxX = _fixedRowRect.x + _fixedRowRect.width - 1;
3500 if (_cols != null && _cols.size() > 0) {
3501 IColumn lastCol = _cols.get(_cols.size() - 1);
3502 int mx = xForCol(lastCol) + _tvs.getColumnWidth(lastCol);
3503 maxX = mx;
3504 }
3505 gc.setClipping(new Rectangle(0, 0, width, height));
3506 int fy = _fixedRowRect.y + _fixedRowRect.height - 1;
3507 gc.drawLine(_fixedRowRect.x, fy, maxX, fy);
3508 gc.setClipping(clipSave);
3509 }
3510
3511 }
3512
3513 /***
3514 * Draw a single cell. Drawing is accomplished by the associated cell renderer. However the mark for fill dragging
3515 * is drawn by this method.
3516 *
3517 * @param gc gc
3518 * @param area drawing area the cell takes up
3519 * @param row row of the cell
3520 * @param col olumn of the cell
3521 */
3522 private void drawCell(GC gc, Rectangle area, IRow row, IColumn col) {
3523 ICellStyle bc = _tvs.getCellStyle(row, col);
3524 ICellRenderer cellRenderer = getCellRenderer(row, col);
3525
3526 boolean hasFocus = false;
3527 if (_focussedRow == row && _focussedColumn == col) {
3528 hasFocus = true;
3529 }
3530 boolean isSelected = isSelected(row, col);
3531 cellRenderer.draw(gc, this, bc, area, row, col, hasFocus, isSelected, false);
3532 if (_supportFillDragging && isSelected && isDragMarkerCell(row, col)) {
3533 drawFillDragMark(gc, area);
3534 }
3535 }
3536
3537 /*** if a rectangular area is selected, this holds the rectangle ofth eindizes. */
3538 private Rectangle _selectedIdxRectangle = null;
3539
3540 /***
3541 * Retrieve the index rectangle of selected cells.
3542 *
3543 * @return rectangel made from the indizes of the selected cells if a rectangular area is selected (all cells)
3544 */
3545 private Rectangle getSelectedRectangle() {
3546 IJaretTableSelection selection = getSelectionModel().getSelection();
3547 if (!selection.isEmpty() && _selectedIdxRectangle == null) {
3548 Set<IJaretTableCell> cells = selection.getAllSelectedCells(getTableModel());
3549 int minx = -1;
3550 int maxx = -1;
3551 int miny = -1;
3552 int maxy = -1;
3553
3554 Map<Integer, Map<Integer, IJaretTableCell>> cellMap = new HashMap<Integer, Map<Integer, IJaretTableCell>>();
3555 for (IJaretTableCell cell : cells) {
3556 Point p = getCellDisplayIdx(cell);
3557 Map<Integer, IJaretTableCell> lineMap = cellMap.get(p.y);
3558 if (lineMap == null) {
3559 lineMap = new HashMap<Integer, IJaretTableCell>();
3560 cellMap.put(p.y, lineMap);
3561 }
3562 if (miny == -1 || p.y < miny) {
3563 miny = p.y;
3564 }
3565 if (maxy == -1 || p.y > maxy) {
3566 maxy = p.y;
3567 }
3568 lineMap.put(p.x, cell);
3569 if (minx == -1 || p.x < minx) {
3570 minx = p.x;
3571 }
3572 if (maxx == -1 || p.x > maxx) {
3573 maxx = p.x;
3574 }
3575 }
3576
3577 boolean everythingSelected = true;
3578 for (int y = miny; y <= maxy && everythingSelected; y++) {
3579 Map<Integer, IJaretTableCell> lineMap = cellMap.get(y);
3580 if (lineMap != null) {
3581 for (int x = minx; x <= maxx; x++) {
3582 IJaretTableCell cell = lineMap.get(x);
3583 if (cell == null) {
3584 everythingSelected = false;
3585 break;
3586 }
3587 }
3588 } else {
3589 everythingSelected = false;
3590 break;
3591 }
3592 }
3593 if (everythingSelected) {
3594 _selectedIdxRectangle = new Rectangle(minx, miny, maxx - minx + 1, maxy - miny + 1);
3595 } else {
3596 _selectedIdxRectangle = null;
3597 }
3598 }
3599 return _selectedIdxRectangle;
3600 }
3601
3602 /***
3603 * Check whether a cell is the cell that should currently be marked with the drag fill marker.
3604 *
3605 * @param row row of the cell
3606 * @param col column of the cell
3607 * @return true if it is the cell carrying the drag mark
3608 */
3609 private boolean isDragMarkerCell(IRow row, IColumn col) {
3610 Rectangle selIdxRect = getSelectedRectangle();
3611 if (selIdxRect != null) {
3612 if (selIdxRect.width == 1 || selIdxRect.height == 1) {
3613 int x = getColumnIdx(col);
3614 int y = getRowIdx(row);
3615 if (x == selIdxRect.x + selIdxRect.width - 1 && y == selIdxRect.y + selIdxRect.height - 1) {
3616 return true;
3617 }
3618 }
3619 }
3620
3621 return false;
3622 }
3623
3624 /***
3625 * Draws the fill drag mark.
3626 *
3627 * @param gc GC
3628 * @param area drawing area of the cell carrying the marker
3629 */
3630 private void drawFillDragMark(GC gc, Rectangle area) {
3631 Color bg = gc.getBackground();
3632 gc.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_BLACK));
3633 _dragMarkerRect = new Rectangle(area.x + area.width - FILLDRAGMARKSIZE,
3634 area.y + area.height - FILLDRAGMARKSIZE, FILLDRAGMARKSIZE, FILLDRAGMARKSIZE);
3635 gc.fillRectangle(_dragMarkerRect);
3636 gc.setBackground(bg);
3637 }
3638
3639 /***
3640 * Draws a thicker border around the fill drag area.
3641 *
3642 * @param gc GC
3643 */
3644 private void drawFillDragBorder(GC gc) {
3645
3646
3647
3648
3649
3650
3651
3652
3653
3654
3655
3656
3657
3658
3659 }
3660
3661 /***
3662 * Set the header drawing height.
3663 *
3664 * @param newHeight height in pixel.
3665 */
3666 public void setHeaderHeight(int newHeight) {
3667 if (newHeight != _headerHeight) {
3668 int oldVal = _headerHeight;
3669 _headerHeight = newHeight;
3670 redraw();
3671 firePropertyChange(PROPERTYNAME_HEADERHEIGHT, oldVal, _headerHeight);
3672 }
3673 }
3674
3675 /***
3676 * Retrieve the header height.
3677 *
3678 * @return header height (pixel)
3679 */
3680 public int getHeaderHeight() {
3681 return _headerHeight;
3682 }
3683
3684 /***
3685 * Total height of all possibly displayed rows (filter applied!).
3686 *
3687 * @return sum of all rowheigths
3688 */
3689 public int getTotalHeight() {
3690 if (_rows != null) {
3691 int h = 0;
3692 for (IRow row : _rows) {
3693 h += _tvs.getRowHeight(row);
3694 }
3695 return h;
3696 } else {
3697 return 0;
3698 }
3699 }
3700
3701 /***
3702 * Total height of the first n rows.
3703 *
3704 * @param numRows number of rows to sum up the heights of
3705 * @return sum of the first first n rowheights
3706 */
3707 public int getTotalHeight(int numRows) {
3708 if (_rows != null) {
3709 int h = 0;
3710 for (int i = 0; i < numRows; i++) {
3711 IRow row = _rows.get(i);
3712 h += _tvs.getRowHeight(row);
3713 }
3714 return h;
3715 } else {
3716 return 0;
3717 }
3718 }
3719
3720 /***
3721 * Retrieve total width of all possibly displayed columns.
3722 *
3723 * @return sum of colwidhts
3724 */
3725 public int getTotalWidth() {
3726 if (_cols != null) {
3727 int width = 0;
3728 for (IColumn col : _cols) {
3729 width += _tvs.getColumnWidth(col);
3730 }
3731 return width;
3732 } else {
3733 return 0;
3734 }
3735 }
3736
3737 /***
3738 * Retrieve total width of the first n columns.
3739 *
3740 * @param n number of colums to take into account
3741 * @return sum of the first n column withs
3742 */
3743 public int getTotalWidth(int n) {
3744 if (_cols != null) {
3745 int width = 0;
3746 for (int i = 0; i < n; i++) {
3747 IColumn col = _cols.get(i);
3748 width += _tvs.getColumnWidth(col);
3749 }
3750 return width;
3751 } else {
3752 return 0;
3753 }
3754 }
3755
3756 /***
3757 * Calculate the width of all fixed columns.
3758 *
3759 * @return the sum of the individual widths of the fixed columns
3760 */
3761 private int getFixedColumnsWidth() {
3762 int w = 0;
3763 for (int i = 0; i < _fixedColumns; i++) {
3764 w += _tvs.getColumnWidth(_cols.get(i));
3765 }
3766 return w;
3767 }
3768
3769 /***
3770 * Calculate the height of all fixed rows.
3771 *
3772 * @return sum of the individual heights of the fixed rows
3773 */
3774 private int getFixedRowsHeight() {
3775 int h = 0;
3776 for (int i = 0; i < _fixedRows; i++) {
3777 h += _tvs.getRowHeight(_rows.get(i));
3778 }
3779 return h;
3780 }
3781
3782 /***
3783 * Retrieve the width of the control.
3784 *
3785 * @return width in pixel
3786 */
3787 public int getWidth() {
3788 return getClientArea().width;
3789 }
3790
3791 /***
3792 * Retrieve the height of the control.
3793 *
3794 * @return height in pixel
3795 */
3796 public int getHeight() {
3797 return getClientArea().height;
3798 }
3799
3800 /***
3801 * Retrieve the table viewstate.
3802 *
3803 * @return the tvs.
3804 */
3805 public ITableViewState getTableViewState() {
3806 return _tvs;
3807 }
3808
3809 /***
3810 * Set a TableViewState.
3811 *
3812 * @param tvs The tvs to set.
3813 */
3814 public void setTableViewState(ITableViewState tvs) {
3815 if (_tvs != null) {
3816 _tvs.removeTableViewStateListener(this);
3817 }
3818 _tvs = tvs;
3819 _tvs.addTableViewStateListener(this);
3820 }
3821
3822
3823 /***
3824 * {@inheritDoc}
3825 */
3826 public void rowHeightChanged(IRow row, int newHeight) {
3827 Rectangle r = getRowBounds(row);
3828 if (r != null) {
3829 int height = getHeight() - r.y;
3830 redraw(r.x, r.y, r.width, height, true);
3831 _rowInfoCache = null;
3832 }
3833 updateYScrollBar();
3834 }
3835
3836 /***
3837 * {@inheritDoc}
3838 */
3839 public void rowHeightModeChanged(IRow row, RowHeightMode newHeightMode) {
3840 if (isDisplayed(row)) {
3841 if (newHeightMode == RowHeightMode.OPTANDVAR || newHeightMode == RowHeightMode.OPTIMAL) {
3842 optimizeHeight(row);
3843 }
3844 redraw();
3845 }
3846
3847
3848
3849 if (newHeightMode != RowHeightMode.OPTANDVAR && newHeightMode != RowHeightMode.OPTIMAL) {
3850 doNotOptimizeHeight(row);
3851 }
3852 }
3853
3854 /***
3855 * {@inheritDoc}
3856 */
3857 public void columnWidthChanged(IColumn column, int newWidth) {
3858 registerRowsForOptimization();
3859 Rectangle r = getColumnBounds(column);
3860 if (_headerRect != null) {
3861 int y = _drawHeader ? _headerRect.y : r.y;
3862 int width = getWidth() - r.x;
3863 int height = _drawHeader ? _headerRect.height + r.height : r.height;
3864 if (_autoFilterEnabled) {
3865 height += _autoFilterRect.height;
3866 }
3867 redraw(r.x, y, width, height, true);
3868 }
3869 updateXScrollBar();
3870 }
3871
3872 /***
3873 * {@inheritDoc}
3874 */
3875 public void columnWidthsChanged() {
3876 registerRowsForOptimization();
3877 redraw();
3878 updateXScrollBar();
3879 }
3880
3881
3882
3883
3884
3885
3886
3887
3888
3889
3890
3891
3892
3893
3894
3895
3896
3897
3898
3899
3900 /***
3901 * {@inheritDoc}
3902 */
3903 public void columnVisibilityChanged(IColumn column, boolean visible) {
3904 updateColumnList();
3905 updateXScrollBar();
3906 redraw();
3907 }
3908
3909 /***
3910 * {@inheritDoc}
3911 */
3912 public void sortingChanged() {
3913 updateRowList();
3914 redraw();
3915
3916 firePropertyChange(PROPERTYNAME_SORTING, null, "x");
3917 }
3918
3919 /***
3920 * {@inheritDoc}
3921 */
3922 public void columnOrderChanged() {
3923 updateColumnList();
3924 redraw();
3925 }
3926
3927 /***
3928 * {@inheritDoc}
3929 */
3930 public void cellStyleChanged(IRow row, IColumn column, ICellStyle style) {
3931 if (column == null) {
3932 redraw(row);
3933 } else if (row == null) {
3934 redraw(column);
3935 } else {
3936 redraw(row, column);
3937 }
3938 }
3939
3940
3941
3942 /***
3943 * Set the enabled state for the autofilter.
3944 *
3945 * @param enable true for enabling the autofilter
3946 */
3947 public void setAutoFilterEnable(boolean enable) {
3948 if (_autoFilterEnabled != enable) {
3949 _autoFilterEnabled = enable;
3950 if (enable) {
3951 updateAutoFilter();
3952 }
3953 redraw();
3954 updateYScrollBar();
3955 preparePaint(getWidth(), getHeight());
3956 firePropertyChange(PROPERTYNAME_AUTOFILTERENABLE, !enable, enable);
3957 }
3958 }
3959
3960 /***
3961 * Retrieve the autofilter state.
3962 *
3963 * @return true for anabled autofilter
3964 */
3965 public boolean getAutoFilterEnable() {
3966 return _autoFilterEnabled;
3967 }
3968
3969 /***
3970 * Create and/or update autofilters.
3971 *
3972 */
3973 private void updateAutoFilter() {
3974 if (_autoFilterEnabled) {
3975
3976 if (_autoFilter == null) {
3977 _autoFilter = new AutoFilter(this);
3978 }
3979
3980 for (IColumn column : _cols) {
3981 if (_autoFilterMap.get(column) == null) {
3982 IAutoFilter af = createAutoFilter(column);
3983 if (af != null) {
3984 af.addPropertyChangeListener(_autoFilter);
3985 _autoFilterMap.put(column, af);
3986 }
3987 }
3988 }
3989
3990
3991 for (IColumn column : _cols) {
3992 IAutoFilter af = _autoFilterMap.get(column);
3993 if (af != null) {
3994 af.update();
3995 }
3996 }
3997
3998 }
3999 }
4000
4001 /***
4002 * Instantiate an autofilter instance for the given column.
4003 *
4004 * @param column column
4005 * @return instantiated autofilter or <code>null</code> if any error occurs during instantiation
4006 */
4007 private IAutoFilter createAutoFilter(IColumn column) {
4008 Class<? extends IAutoFilter> clazz = getAutoFilterClass(column);
4009 if (clazz == null) {
4010 return null;
4011 }
4012 IAutoFilter result = null;
4013 try {
4014 Constructor<? extends IAutoFilter> constructor = clazz.getConstructor();
4015 result = constructor.newInstance();
4016 } catch (Exception e) {
4017 e.printStackTrace();
4018 return null;
4019 }
4020
4021 result.setTable(this);
4022 result.setColumn(column);
4023 return result;
4024 }
4025
4026 /***
4027 * Retrieve the state of header drawing.
4028 *
4029 * @return true when headers are drawn.
4030 */
4031 public boolean getDrawHeader() {
4032 return _drawHeader;
4033 }
4034
4035 /***
4036 * If set to true, the header row will be drawn.
4037 *
4038 * @param drawHeader true: draw the header
4039 */
4040 public void setDrawHeader(boolean drawHeader) {
4041 if (_drawHeader != drawHeader) {
4042 _drawHeader = drawHeader;
4043 redraw();
4044 }
4045 }
4046
4047 /***
4048 * @return Returns the headerRenderer.
4049 */
4050 public ITableHeaderRenderer getHeaderRenderer() {
4051 return _headerRenderer;
4052 }
4053
4054 /***
4055 * Set a header renderer.
4056 *
4057 * @param headerRenderer The headerRenderer to set.
4058 */
4059 public void setHeaderRenderer(ITableHeaderRenderer headerRenderer) {
4060 _headerRenderer = headerRenderer;
4061 redraw();
4062 }
4063
4064 /***
4065 * @return Returns the columnResizeAllowed.
4066 */
4067 public boolean isColumnResizeAllowed() {
4068 return _columnResizeAllowed;
4069 }
4070
4071 /***
4072 * Set whether column resizing is allowed.
4073 *
4074 * @param columnResizeAllowed true for allowing col resizing.
4075 */
4076 public void setColumnResizeAllowed(boolean columnResizeAllowed) {
4077 _columnResizeAllowed = columnResizeAllowed;
4078 }
4079
4080 /***
4081 * @return Returns the headerResizeAllowed.
4082 */
4083 public boolean isHeaderResizeAllowed() {
4084 return _headerResizeAllowed;
4085 }
4086
4087 /***
4088 * @param headerResizeAllowed The headerResizeAllowed to set.
4089 */
4090 public void setHeaderResizeAllowed(boolean headerResizeAllowed) {
4091 _headerResizeAllowed = headerResizeAllowed;
4092 }
4093
4094 /***
4095 * @return Returns the rowResizeAllowed.
4096 */
4097 public boolean isRowResizeAllowed() {
4098 return _rowResizeAllowed;
4099 }
4100
4101 /***
4102 * @param rowResizeAllowed The rowResizeAllowed to set.
4103 */
4104 public void setRowResizeAllowed(boolean rowResizeAllowed) {
4105 _rowResizeAllowed = rowResizeAllowed;
4106 }
4107
4108 /***
4109 * Set the first row displayed.
4110 *
4111 * @param idx index of the first row to be displayed.
4112 * @param pixeloffset the pixeloffset of the first row
4113 */
4114 public void setFirstRow(int idx, int pixeloffset) {
4115 if (_firstRowIdx != idx || _firstRowPixelOffset != pixeloffset) {
4116 int oldFirstIdx = _firstRowIdx;
4117 int oldPixelOffset = _firstRowPixelOffset;
4118
4119 _firstRowIdx = idx;
4120 _firstRowPixelOffset = pixeloffset;
4121 _rowInfoCache = null;
4122 updateYScrollBar();
4123 redraw();
4124 firePropertyChange(PROPERTYNAME_FIRSTROWIDX, oldFirstIdx, idx);
4125 firePropertyChange(PROPERTYNAME_FIRSTROWPIXELOFFSET, oldPixelOffset, pixeloffset);
4126 }
4127 }
4128
4129 /***
4130 * Internal row filter pooling the results of the autofilters.
4131 *
4132 * @author Peter Kliem
4133 * @version $Id: JaretTable.java 1076 2010-12-05 13:34:42Z kliem $
4134 */
4135 private class AutoFilter extends PropertyObservableBase implements IRowFilter, PropertyChangeListener {
4136 /*** the table instance of the filter. */
4137 private JaretTable _table;
4138
4139 public AutoFilter(JaretTable table) {
4140 _table = table;
4141 }
4142
4143 /***
4144 * {@inheritDoc}
4145 */
4146 public boolean isInResult(IRow row) {
4147 boolean result = true;
4148 for (IColumn column : _cols) {
4149 IAutoFilter af = _autoFilterMap.get(column);
4150 if (af != null) {
4151 result = result && af.isInResult(row);
4152 }
4153 if (!result) {
4154 break;
4155 }
4156 }
4157 return result;
4158 }
4159
4160 /***
4161 * {@inheritDoc} Whenever a filter signals change update everything.
4162 */
4163 public void propertyChange(PropertyChangeEvent evt) {
4164 updateRowList();
4165 setFirstRow(_fixedRows, 0);
4166 redraw();
4167
4168 _table.firePropertyChange(PROPERTYNAME_FILTERING, null, "x");
4169 }
4170 }
4171
4172 /***
4173 * If a header context is present, display it at x,y.
4174 *
4175 * @param x x coordinate
4176 * @param y y coordinate
4177 */
4178 public void displayHeaderContextMenu(int x, int y) {
4179 if (_headerContextMenu != null) {
4180 dispContextMenu(_headerContextMenu, x, y);
4181 }
4182 }
4183
4184 /***
4185 * If a row context is present, display it at x,y.
4186 *
4187 * @param x x coordinate
4188 * @param y y coordinate
4189 */
4190 public void displayRowContextMenu(int x, int y) {
4191 if (_rowContextMenu != null) {
4192 dispContextMenu(_rowContextMenu, x, y);
4193 }
4194 }
4195
4196
4197 private void dispContextMenu(Menu contextMenu, int x, int y) {
4198 Shell shell = Display.getCurrent().getActiveShell();
4199 Point coords = Display.getCurrent().map(this, shell, x, y);
4200 contextMenu.setLocation(coords.x + shell.getLocation().x, coords.y + shell.getLocation().y);
4201 contextMenu.setVisible(true);
4202 }
4203
4204 /***
4205 * Set a context menu to be displayed on the header area.
4206 *
4207 * @param headerCtxMenu menu to display on the header or <code>null</code> to disable
4208 */
4209 public void setHeaderContextMenu(Menu headerCtxMenu) {
4210 _headerContextMenu = headerCtxMenu;
4211 }
4212
4213 /***
4214 * Retrieve a context menu that has been set on the header.
4215 *
4216 * @return context menu or <code>null</code>
4217 */
4218 public Menu getHeaderContextMenu() {
4219 return _headerContextMenu;
4220 }
4221
4222 /***
4223 * Set a context menu to be displayed on rows.
4224 *
4225 * @param rowCtxMenu context menu or <code>null</code> to disable
4226 */
4227 public void setRowContextMenu(Menu rowCtxMenu) {
4228 _rowContextMenu = rowCtxMenu;
4229 }
4230
4231 /***
4232 * Retrieve a context menu that has been set for the rows.
4233 *
4234 * @return context menu or <code>null</code>
4235 */
4236 public Menu getRowContextMenu() {
4237 return _rowContextMenu;
4238 }
4239
4240 /***
4241 * @return Returns the fixedColumns.
4242 */
4243 public int getFixedColumns() {
4244 return _fixedColumns;
4245 }
4246
4247 /***
4248 * Set the numerb of fixed columns. Fixed columns are excluded from vertial scrolling. Row resizing can be
4249 * restricted to the area of the fixed columns.
4250 *
4251 * @param fixedColumns The fixedColumns to set.
4252 */
4253 public void setFixedColumns(int fixedColumns) {
4254 if (_fixedColumns != fixedColumns) {
4255 _fixedColumns = fixedColumns;
4256 _firstColIdx = fixedColumns;
4257 _firstColPixelOffset = 0;
4258 redraw();
4259 }
4260 }
4261
4262 /***
4263 * @return Returns the fixedRows.
4264 */
4265 public int getFixedRows() {
4266 return _fixedRows;
4267 }
4268
4269 /***
4270 * Set the number of rows to be fixed (excluded from scrolling and autofiltering; optionally from sorting).
4271 *
4272 * @param fixedRows The fixedRows to set.
4273 */
4274 public void setFixedRows(int fixedRows) {
4275 if (_fixedRows != fixedRows) {
4276 _fixedRows = fixedRows;
4277 _firstRowIdx = fixedRows;
4278 _firstRowPixelOffset = 0;
4279 _rowInfoCache = null;
4280 redraw();
4281 }
4282 }
4283
4284
4285
4286 /***
4287 * {@inheritDoc}
4288 */
4289 public void rowChanged(IRow row) {
4290 if (_tvs.getRowHeigthMode(row) == ITableViewState.RowHeightMode.OPTIMAL
4291 || _tvs.getRowHeigthMode(row) == ITableViewState.RowHeightMode.OPTANDVAR) {
4292 optimizeHeight(row);
4293 }
4294 redraw(row);
4295 }
4296
4297 /***
4298 * {@inheritDoc}
4299 */
4300 public void rowRemoved(IRow row) {
4301
4302 updateRowList();
4303 if (isDisplayed(row)) {
4304 syncedRedraw();
4305 }
4306 updateYScrollBar();
4307 }
4308
4309 /***
4310 * {@inheritDoc}
4311 */
4312 public void rowAdded(int idx, IRow row) {
4313 updateRowList();
4314 syncedRedraw();
4315 updateYScrollBar();
4316 }
4317
4318 /***
4319 * {@inheritDoc}
4320 */
4321 public void columnAdded(int idx, IColumn column) {
4322 _tvs.getSortedColumns().add(column);
4323 updateColumnList();
4324 syncedRedraw();
4325 updateXScrollBar();
4326 }
4327
4328 /***
4329 * {@inheritDoc}
4330 */
4331 public void columnRemoved(IColumn column) {
4332
4333 _tvs.getSortedColumns().remove(column);
4334 updateColumnList();
4335 if (isDisplayed(column)) {
4336 syncedRedraw();
4337 }
4338 updateXScrollBar();
4339 }
4340
4341 /***
4342 * {@inheritDoc}
4343 */
4344 public void columnChanged(IColumn column) {
4345 redraw(column);
4346 }
4347
4348 /***
4349 * {@inheritDoc}
4350 */
4351 public void cellChanged(IRow row, IColumn column) {
4352 redraw(row, column);
4353 }
4354
4355 /***
4356 * {@inheritDoc}
4357 */
4358 public void tableDataChanged() {
4359
4360 updateRowList();
4361 syncedRedraw();
4362 }
4363
4364
4365
4366 /***
4367 * Retrieve the flag controlling whether fixed rows are excluded from sorting.
4368 *
4369 * @return if true fixed rows will not be affected by sorting operations
4370 */
4371 public boolean getExcludeFixedRowsFromSorting() {
4372 return _excludeFixedRowsFromSorting;
4373 }
4374
4375 /***
4376 * If set to true, fixed rows are exluded from sorting.
4377 *
4378 * @param excludeFixedRowsFromSorting true for exclude fixed rows from sorting.
4379 */
4380 public void setExcludeFixedRowsFromSorting(boolean excludeFixedRowsFromSorting) {
4381 _excludeFixedRowsFromSorting = excludeFixedRowsFromSorting;
4382 }
4383
4384 /***
4385 * Get the state of the resize restriction flag. If true, resizing is only allowed in header and fixed columns (for
4386 * rows) and the leftmost SELDELTA pixels of eachrow.
4387 *
4388 * @return Returns the resizeRestriction.
4389 */
4390 public boolean getResizeRestriction() {
4391 return _resizeRestriction;
4392 }
4393
4394 /***
4395 * If set to true resizing of columns will only be allowed in the header area. Row resizing will be allowed on fixed
4396 * columns and on the first SEL_DELTA pixels of the leftmost column when restricted.
4397 *
4398 * @param resizeRestriction The resizeRestriction to set.
4399 */
4400 public void setResizeRestriction(boolean resizeRestriction) {
4401 _resizeRestriction = resizeRestriction;
4402 }
4403
4404
4405
4406 /***
4407 * {@inheritDoc}
4408 */
4409 public void rowSelectionAdded(IRow row) {
4410 _selectedIdxRectangle = null;
4411 redraw(row);
4412 }
4413
4414 /***
4415 * {@inheritDoc}
4416 */
4417 public void rowSelectionRemoved(IRow row) {
4418 _selectedIdxRectangle = null;
4419 redraw(row);
4420 }
4421
4422 /***
4423 * {@inheritDoc}
4424 */
4425 public void cellSelectionAdded(IJaretTableCell cell) {
4426 _selectedIdxRectangle = null;
4427 redraw(cell.getRow(), cell.getColumn());
4428 }
4429
4430 /***
4431 * {@inheritDoc}
4432 */
4433 public void cellSelectionRemoved(IJaretTableCell cell) {
4434 _selectedIdxRectangle = null;
4435 redraw(cell.getRow(), cell.getColumn());
4436 }
4437
4438 /***
4439 * {@inheritDoc}
4440 */
4441 public void columnSelectionAdded(IColumn column) {
4442 _selectedIdxRectangle = null;
4443 redraw(column);
4444 }
4445
4446 /***
4447 * {@inheritDoc}
4448 */
4449 public void columnSelectionRemoved(IColumn column) {
4450 _selectedIdxRectangle = null;
4451 redraw(column);
4452 }
4453
4454
4455
4456 /***
4457 * Retrieve the selectionmodel used by the table.
4458 *
4459 * @return the selection model
4460 */
4461 public IJaretTableSelectionModel getSelectionModel() {
4462 return _selectionModel;
4463 }
4464
4465 /***
4466 * Set the selection model to be used by the table.
4467 *
4468 * @param jts the selection model to be used (usually the default implementation)
4469 */
4470 public void setSelectionModel(IJaretTableSelectionModel jts) {
4471 if (_selectionModel != null) {
4472 _selectionModel.removeTableSelectionModelListener(this);
4473 }
4474 _selectionModel = jts;
4475 _selectionModel.addTableSelectionModelListener(this);
4476 }
4477
4478 /***
4479 * Current number of displayed columns.
4480 *
4481 * @return number of displayed columns
4482 */
4483 public int getColumnCount() {
4484 return _cols.size();
4485 }
4486
4487 /***
4488 * Retrieve column by the display idx.
4489 *
4490 * @param idx display idx
4491 * @return column
4492 */
4493 public IColumn getColumn(int idx) {
4494 return _cols.get(idx);
4495 }
4496
4497 /***
4498 * Convenience method to retrieve a column by it's id from the model.
4499 *
4500 * @param id id of the column
4501 * @return column or <code>null</code>
4502 */
4503 public IColumn getColumn(String id) {
4504 for (int i = 0; i < _model.getColumnCount(); i++) {
4505 if (_model.getColumn(i).getId().equals(id)) {
4506 return _model.getColumn(i);
4507 }
4508 }
4509 return null;
4510 }
4511
4512 /***
4513 * Get the number of displayed rows (after filtering!).
4514 *
4515 * @return number of displayed rows
4516 */
4517 public int getRowCount() {
4518 return _rows.size();
4519 }
4520
4521 /***
4522 * Get a row by the display idx.
4523 *
4524 * @param idx index in the list of displayed rows.
4525 * @return row
4526 */
4527 public IRow getRow(int idx) {
4528 return _rows.get(idx);
4529 }
4530
4531 /***
4532 * @return Returns the rowFilter.
4533 */
4534 public IRowFilter getRowFilter() {
4535 return _rowFilter;
4536 }
4537
4538 /***
4539 * Set a row filter on the table.
4540 *
4541 * @param rowFilter The rowFilter to set.
4542 */
4543 public void setRowFilter(IRowFilter rowFilter) {
4544 IRowFilter oldVal = _rowFilter;
4545 if (_rowFilter != null) {
4546 _rowFilter.removePropertyChangeListener(this);
4547 }
4548 _rowFilter = rowFilter;
4549 if (_rowFilter != null) {
4550 _rowFilter.addPropertyChangeListener(this);
4551 }
4552 updateRowList();
4553 updateAutoFilter();
4554 redraw();
4555 firePropertyChange(PROPERTYNAME_ROWFILTER, oldVal, _rowFilter);
4556
4557 firePropertyChange(PROPERTYNAME_FILTERING, null, "x");
4558 }
4559
4560 /***
4561 * @return Returns the rowSorter.
4562 */
4563 public IRowSorter getRowSorter() {
4564 return _rowSorter;
4565 }
4566
4567 /***
4568 * Set a row sorter. A row sorter will be overruled by sorting setup on columns.
4569 *
4570 * @param rowSorter The rowSorter to set.
4571 */
4572 public void setRowSorter(IRowSorter rowSorter) {
4573 IRowSorter oldValue = _rowSorter;
4574 if (_rowSorter != null) {
4575 _rowSorter.removePropertyChangeListener(this);
4576 }
4577 _rowSorter = rowSorter;
4578 if (_rowSorter != null) {
4579 _rowSorter.addPropertyChangeListener(this);
4580 }
4581 updateRowList();
4582 redraw();
4583
4584 firePropertyChange(PROPERTYNAME_ROWSORTER, oldValue, _rowSorter);
4585
4586 firePropertyChange(PROPERTYNAME_SORTING, null, "x");
4587 }
4588
4589 /***
4590 * Add a listener to listen for focus changes in the table (focussed cell).
4591 *
4592 * @param tfl listener
4593 */
4594 public synchronized void addTableFocusListener(ITableFocusListener tfl) {
4595 if (_tableFocusListeners == null) {
4596 _tableFocusListeners = new Vector<ITableFocusListener>();
4597 }
4598 _tableFocusListeners.add(tfl);
4599 }
4600
4601 /***
4602 * Remove a registered listener.
4603 *
4604 * @param tfl listener
4605 */
4606 public synchronized void remTableFocusListener(ITableFocusListener tfl) {
4607 if (_tableFocusListeners != null) {
4608 _tableFocusListeners.remove(tfl);
4609 }
4610 }
4611
4612 /***
4613 * Inform focus listeners about a change of the focussed cell.
4614 *
4615 * @param row row of the focussed cell
4616 * @param column column of the focussed cell
4617 */
4618 private void fireTableFocusChanged(IRow row, IColumn column) {
4619 if (_tableFocusListeners != null) {
4620 for (ITableFocusListener listener : _tableFocusListeners) {
4621 listener.tableFocusChanged(this, row, column);
4622 }
4623 }
4624 }
4625
4626
4627 /***
4628 * {@inheritDoc} The table eitselflistens for prop changes of the rowSorter and the rowFilter.
4629 */
4630 public void propertyChange(PropertyChangeEvent evt) {
4631 if (evt.getSource().equals(_rowSorter)) {
4632 updateRowList();
4633 updateAutoFilter();
4634 redraw();
4635 firePropertyChange(PROPERTYNAME_SORTING, null, "x");
4636 } else if (evt.getSource().equals(_rowFilter)) {
4637 updateRowList();
4638 updateAutoFilter();
4639 redraw();
4640 firePropertyChange(PROPERTYNAME_FILTERING, null, "x");
4641 }
4642
4643 }
4644
4645
4646
4647 /***
4648 * Retrieve the used startegy when performing a fill drag.
4649 *
4650 * @return the fillDragStrategy
4651 */
4652 public IFillDragStrategy getFillDragStrategy() {
4653 return _fillDragStrategy;
4654 }
4655
4656 /***
4657 * Set the strategy used when perfoming a fill drag.
4658 *
4659 * @param fillDragStrategy the fillDragStrategy to set. Must be non null.
4660 */
4661 public void setFillDragStrategy(IFillDragStrategy fillDragStrategy) {
4662 if (fillDragStrategy == null) {
4663 throw new IllegalArgumentException("FillDragStrategy must not be NULL");
4664 }
4665 _fillDragStrategy = fillDragStrategy;
4666 }
4667
4668 /***
4669 * Retrieve whether fill dragging is activated.
4670 *
4671 * @return the supportFillDragging
4672 */
4673 public boolean isSupportFillDragging() {
4674 return _supportFillDragging;
4675 }
4676
4677 /***
4678 * Set fill drag activation.
4679 *
4680 * @param supportFillDragging the supportFillDragging to set
4681 */
4682 public void setSupportFillDragging(boolean supportFillDragging) {
4683 _supportFillDragging = supportFillDragging;
4684 _dragMarkerRect = null;
4685 redraw();
4686 }
4687
4688 /***
4689 * @return the iccpStrategy
4690 */
4691 public ICCPStrategy getCcpStrategy() {
4692 return _ccpStrategy;
4693 }
4694
4695 /***
4696 * Set the strategy to perform cut, copy, paste operations. Setting the strategy to <code>null</code> causes
4697 * deactivation of ccp.
4698 *
4699 * @param ccpStrategy the iccpStrategy to set or <code>null</code> to deactivat ccp
4700 */
4701 public void setCcpStrategy(ICCPStrategy ccpStrategy) {
4702 _ccpStrategy = ccpStrategy;
4703 }
4704
4705 /***
4706 * Do a cut operation. Implementation is supplied by the CCPStrategy.
4707 *
4708 */
4709 public void cut() {
4710 if (_ccpStrategy != null) {
4711 _ccpStrategy.cut(this);
4712 }
4713 }
4714
4715 /***
4716 * Do a copy operation. Implementation is supplied by the CCPStrategy.
4717 *
4718 */
4719 public void copy() {
4720 if (_ccpStrategy != null) {
4721 _ccpStrategy.copy(this);
4722 }
4723 }
4724
4725 /***
4726 * Do a paste operation. Implementation is supplied by the CCPStrategy.
4727 *
4728 */
4729 public void paste() {
4730 if (_ccpStrategy != null) {
4731 _ccpStrategy.paste(this);
4732 }
4733 }
4734
4735 /***
4736 * Select all cells by selectiong all displayed (not filtered) columns.
4737 *
4738 */
4739 public void selectAll() {
4740 getSelectionModel().clearSelection();
4741 for (IColumn col : _cols) {
4742 getSelectionModel().addSelectedColumn(col);
4743 }
4744 }
4745
4746 /***
4747 * Retrieve whether scroll opotimizations are active.
4748 *
4749 * @return true if scrolling is done optimized
4750 */
4751 public boolean getOptimizeScrolling() {
4752 return _optimizeScrolling;
4753 }
4754
4755 /***
4756 * Set whether to use optimized scrolling by copying content. Defaults to false (since it causes trouble when
4757 * running on Linux or OSX).
4758 *
4759 * @param optimizeScrolling true for optimizing
4760 */
4761 public void setOptimizeScrolling(boolean optimizeScrolling) {
4762 _optimizeScrolling = optimizeScrolling;
4763 }
4764
4765 /***
4766 * Retrieve the height used to render the autofilters.
4767 *
4768 * @return height of the autofilter rectangle
4769 */
4770 public int getAutoFilterHeight() {
4771 return _autoFilterEnabled ? _autoFilterRect.height : 0;
4772 }
4773
4774 /***
4775 * {@inheritDoc}
4776 */
4777 public void addPropertyChangeListener(PropertyChangeListener listener) {
4778 _propertyChangeSupport.addPropertyChangeListener(listener);
4779 }
4780
4781 /***
4782 * {@inheritDoc}
4783 */
4784 public void removePropertyChangeListener(PropertyChangeListener listener) {
4785 _propertyChangeSupport.removePropertyChangeListener(listener);
4786 }
4787
4788 /***
4789 * {@inheritDoc}
4790 */
4791 public void firePropertyChange(String propName, Object oldVal, Object newVal) {
4792 if (_propertyChangeSupport != null) {
4793 _propertyChangeSupport.firePropertyChange(propName, oldVal, newVal);
4794 }
4795 }
4796
4797 /***
4798 * Retrieve the pixel offset the first row is scrolled.
4799 *
4800 * @return pixel ofset of the first row
4801 */
4802 public int getFirstRowPixelOffset() {
4803 return _firstRowPixelOffset;
4804 }
4805
4806 /***
4807 * Retrive the index of the first row displayed in the scrolled area of the table.
4808 *
4809 * @return index of the first row displayed
4810 */
4811 public int getFirstRowIdx() {
4812 return _firstRowIdx;
4813 }
4814
4815 /***
4816 * Check whether sorting the table is allowed.
4817 *
4818 * @return true if sorting is allowed
4819 */
4820 public boolean getAllowSorting() {
4821 return _allowSorting;
4822 }
4823
4824 /***
4825 * Set the global allowance for sorting. This defaults to true.
4826 *
4827 * @param allowSorting true to allow sorting
4828 */
4829 public void setAllowSorting(boolean allowSorting) {
4830 _allowSorting = allowSorting;
4831 }
4832
4833 /***
4834 * Get access to the internal row list. This is for special purposes (like synchronizing models) only. Use with
4835 * care!
4836 *
4837 * @return the internal list of rows
4838 */
4839 public List<IRow> getInternalRowList() {
4840 return _rows;
4841 }
4842
4843 }