/* * File : TableCellImpl.java * Created : 24 nov. 2003 * By : Olivier * Originally PluginItem.java, and changed to be more generic. * * Copyright (C) 2004, 2005, 2006 Aelitis SAS, All rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details ( see the LICENSE file ). * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * AELITIS, SAS au capital de 46,603.30 euros, * 8 Allee Lenotre, La Grille Royale, 78600 Le Mesnil le Roi, France. */ package org.gudy.azureus2.ui.swt.views.table.impl; import java.text.Collator; import java.util.ArrayList; import java.util.Comparator; import java.util.Iterator; import java.util.Locale; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.*; import org.gudy.azureus2.core3.config.COConfigurationManager; import org.gudy.azureus2.core3.config.ParameterListener; import org.gudy.azureus2.core3.internat.MessageText; import org.gudy.azureus2.core3.logging.LogEvent; import org.gudy.azureus2.core3.logging.LogIDs; import org.gudy.azureus2.core3.logging.Logger; import org.gudy.azureus2.core3.util.AEMonitor; import org.gudy.azureus2.core3.util.AERunnable; import org.gudy.azureus2.core3.util.Constants; import org.gudy.azureus2.core3.util.Debug; import org.gudy.azureus2.core3.util.SystemTime; import org.gudy.azureus2.plugins.ui.Graphic; import org.gudy.azureus2.plugins.ui.UIRuntimeException; import org.gudy.azureus2.plugins.ui.SWT.GraphicSWT; import org.gudy.azureus2.plugins.ui.tables.*; import org.gudy.azureus2.ui.swt.Utils; import org.gudy.azureus2.ui.swt.components.*; import org.gudy.azureus2.ui.swt.debug.ObfusticateCellText; import org.gudy.azureus2.ui.swt.mainwindow.Colors; import org.gudy.azureus2.ui.swt.plugins.UISWTGraphic; import org.gudy.azureus2.ui.swt.pluginsimpl.UISWTGraphicImpl; import org.gudy.azureus2.ui.swt.views.table.TableCellSWT; import org.gudy.azureus2.ui.swt.views.table.TableCellSWTPaintListener; import org.gudy.azureus2.ui.swt.views.table.TableRowSWT; import org.gudy.azureus2.ui.swt.views.table.TableViewSWT; import org.gudy.azureus2.ui.swt.views.table.utils.TableColumnSWTUtils; import com.aelitis.azureus.ui.common.table.TableColumnCore; import com.aelitis.azureus.ui.common.table.TableColumnSortObject; import com.aelitis.azureus.ui.common.table.TableRowCore; import com.aelitis.azureus.ui.common.table.TableView; /** TableCellImpl represents one cell in the table. * Table access is provided by BufferedTableItem. * TableCellImpl is stored in and accessed by TableRowCore. * Drawing control gets passed to listeners. * * For plugins, this object is the implementation to TableCell. * * This object is needed to split core code from plugin code. */ public class TableCellImpl implements TableCellSWT { private static final LogIDs LOGID = LogIDs.GUI; private static final byte FLAG_VALID = 1; private static final byte FLAG_SORTVALUEISTEXT = 2; private static final byte FLAG_TOOLTIPISAUTO = 4; /** * For refreshing, this flag manages whether the row is actually up to date. * * We don't update any visuals while the row isn't visible. But, validility * does get set to true so that the cell isn't forced to refresh every * cycle when not visible. (We can't just never call refresh when the row * is not visible, as refresh also sets the sort value) * * When the row does become visible, we have to invalidate the row so * that the row will set its visuals again (this time, actually * updating a viewable object). */ private static final byte FLAG_UPTODATE = 8; private static final byte FLAG_DISPOSED = 16; private static final byte FLAG_MUSTREFRESH = 32; private static final byte FLAG_VISUALLY_CHANGED_SINCE_REFRESH = 64; private byte flags; private TableRowCore tableRow; private Comparable sortValue; private BufferedTableItem bufferedTableItem; private ArrayList refreshListeners; private ArrayList disposeListeners; private ArrayList tooltipListeners; private ArrayList cellMouseListeners; private ArrayList cellMouseMoveListeners; private ArrayList cellVisibilityListeners; private ArrayList cellSWTPaintListeners; private ArrayList<TableCellClipboardListener> cellClipboardListeners; private TableColumnCore tableColumn; private byte refreshErrLoopCount; private byte tooltipErrLoopCount; private byte loopFactor; private Object oToolTip; private Object defaultToolTip; private int iCursorID = SWT.CURSOR_ARROW; private Graphic graphic = null; private ArrayList childCells; public boolean bDebug = false; private static AEMonitor this_mon = new AEMonitor( "TableCell" ); private static final String CFG_PAINT = "GUI_SWT_bAlternateTablePainting"; // Getting the cell's bounds can be slow. QUICK_WIDTH uses TableColumn's width private static final boolean QUICK_WIDTH = true; private static boolean bAlternateTablePainting; private static int MAX_REFRESHES = 10; private static int MAX_REFRESHES_WITHIN_MS = 100; private boolean bInRefresh = false; private long lastRefresh; private int numFastRefreshes; private byte restartRefresh = 0; private boolean bInRefreshAsync = false; private int textAlpha = 255; private Image icon; static { COConfigurationManager.addAndFireParameterListener(CFG_PAINT, new ParameterListener() { public void parameterChanged(String parameterName) { bAlternateTablePainting = COConfigurationManager .getBooleanParameter(CFG_PAINT); } }); } public TableCellImpl(TableRowCore _tableRow, TableColumnCore _tableColumn, int position, BufferedTableItem item) { this.tableColumn = _tableColumn; this.tableRow = _tableRow; flags = FLAG_SORTVALUEISTEXT; refreshErrLoopCount = 0; tooltipErrLoopCount = 0; loopFactor = 0; if (item != null) { bufferedTableItem = item; } else { createBufferedTableItem(position); } tableColumn.invokeCellAddedListeners(TableCellImpl.this); //bDebug = (position == 1) && tableColumn.getTableID().equalsIgnoreCase("Peers"); } /** * Initialize * * @param _tableRow * @param _tableColumn * @param position */ public TableCellImpl(TableRowSWT _tableRow, TableColumnCore _tableColumn, int position) { this(_tableRow, _tableColumn, position, null); } private void createBufferedTableItem(int position) { BufferedTableRow bufRow = (BufferedTableRow)tableRow; if (tableColumn.getType() == TableColumnCore.TYPE_GRAPHIC) { if (bAlternateTablePainting) { bufferedTableItem = new BufferedGraphicTableItem2(bufRow, position) { public void refresh() { TableCellImpl.this.refresh(); } public void invalidate() { clearFlag(FLAG_VALID); redraw(); } }; } else { bufferedTableItem = new BufferedGraphicTableItem1(bufRow, position) { public void refresh() { TableCellImpl.this.refresh(); } public void invalidate() { clearFlag(FLAG_VALID); redraw(); } }; } setOrientationViaColumn(); } else { bufferedTableItem = new BufferedTableItemImpl(bufRow, position) { public void refresh() { TableCellImpl.this.refresh(); } public void invalidate() { clearFlag(FLAG_VALID); } }; } } private void pluginError(Throwable e) { String sTitleLanguageKey = tableColumn.getTitleLanguageKey(); String sPosition = (bufferedTableItem == null) ? "null" : "" + bufferedTableItem.getPosition() + " (" + MessageText.getString(sTitleLanguageKey) + ")"; Logger.log(new LogEvent(LOGID, "Table Cell Plugin for Column #" + sPosition + " generated an exception ", e)); } private void pluginError(String s) { String sTitleLanguageKey = tableColumn.getTitleLanguageKey(); String sPosition = "r" + tableRow.getIndex() + (bufferedTableItem == null ? "null" : "c" + bufferedTableItem.getPosition() + " (" + MessageText.getString(sTitleLanguageKey) + ")"); Logger.log(new LogEvent(LOGID, LogEvent.LT_ERROR, "Table Cell Plugin for Column #" + sPosition + ":" + s + "\n " + Debug.getStackTrace(true, true))); } private void checkCellForSetting() { if ((flags & FLAG_DISPOSED) != 0) { throw new UIRuntimeException("Table Cell is disposed."); } } /* Public API */ //////////////// public Object getDataSource() { // if we've been disposed then row/col are null TableRowCore row = tableRow; TableColumnCore col = tableColumn; if ( row == null || col == null){ return( null ); } return row.getDataSource(col.getUseCoreDataSource()); } public TableColumn getTableColumn() { return tableColumn; } public TableRow getTableRow() { return tableRow; } public String getTableID() { return tableRow.getTableID(); } public boolean isValid() { // Called often.. inline faster return (flags & FLAG_VALID) != 0; //return hasFlag(FLAG_VALID); } public Color getForegroundSWT() { checkCellForSetting(); return bufferedTableItem.getForeground(); } public Color getBackgroundSWT() { checkCellForSetting(); return bufferedTableItem.getBackground(); } public int[] getBackground() { if (bufferedTableItem == null) { return null; } Color color = bufferedTableItem.getBackground(); if (color == null) { return null; } return new int[] { color.getRed(), color.getGreen(), color.getBlue() }; } // @see org.gudy.azureus2.plugins.ui.tables.TableCell#getForeground() public int[] getForeground() { if (bufferedTableItem == null) { return new int[] { 0, 0, 0 }; } Color color = bufferedTableItem.getForeground(); if (color == null) { return new int[3]; } return new int[] { color.getRed(), color.getGreen(), color.getBlue() }; } public boolean setForeground(Color color) { checkCellForSetting(); // Don't need to set when not visible if (isInvisibleAndCanRefresh()) return false; boolean set = bufferedTableItem.setForeground(color); if (set) { setFlag(FLAG_VISUALLY_CHANGED_SINCE_REFRESH); } return set; } public boolean setForeground(int red, int green, int blue) { checkCellForSetting(); // Don't need to set when not visible if (isInvisibleAndCanRefresh()) return false; boolean set; if (red < 0 || green < 0 || blue < 0) { set = bufferedTableItem.setForeground(null); } else { set = bufferedTableItem.setForeground(red, green, blue); } if (set) { setFlag(FLAG_VISUALLY_CHANGED_SINCE_REFRESH); } return set; } // @see org.gudy.azureus2.plugins.ui.tables.TableCell#setForeground(int[]) public boolean setForeground(int[] rgb) { if (rgb == null || rgb.length < 3) { return setForeground((Color) null); } return setForeground(rgb[0], rgb[1], rgb[2]); } public boolean setForegroundToErrorColor() { return setForeground(Colors.colorError); } public boolean setText(String text) { checkCellForSetting(); if (text == null) text = ""; boolean bChanged = false; if (hasFlag(FLAG_SORTVALUEISTEXT) && !text.equals(sortValue)) { bChanged = true; sortValue = text; tableColumn.setLastSortValueChange(SystemTime.getCurrentTime()); if (bDebug) debug("Setting SortValue to text;"); } // Slower than setText(..)! // if (isInvisibleAndCanRefresh()) { // if (bDebug) { // debug("setText ignored: invisible"); // } // return false; // } if (bufferedTableItem.setText(text) && !hasFlag(FLAG_SORTVALUEISTEXT)) bChanged = true; if (bDebug) { debug("setText (" + bChanged + ") : " + text); } if (bChanged) { setFlag(FLAG_VISUALLY_CHANGED_SINCE_REFRESH); } boolean do_auto = this.tableColumn.doesAutoTooltip(); // If we were using auto tooltips (and we aren't any more), then // clear up previously set tooltips. if (!do_auto) { if (hasFlag(FLAG_TOOLTIPISAUTO)) { this.oToolTip = null; clearFlag(FLAG_TOOLTIPISAUTO); } } else { this.oToolTip = text; setFlag(FLAG_TOOLTIPISAUTO); } return bChanged; } private boolean isInvisibleAndCanRefresh() { return !isShown() && (refreshListeners != null || tableColumn.hasCellRefreshListener()); } public String getText() { if (hasFlag(FLAG_SORTVALUEISTEXT) && sortValue instanceof String) return (String)sortValue; if (bufferedTableItem == null) { return null; } return bufferedTableItem.getText(); } public boolean isShown() { if (bufferedTableItem == null) { return false; } return bufferedTableItem.isShown() && tableRow.getView().isColumnVisible(tableColumn); } public boolean setSortValue(Comparable valueToSort) { if (!tableColumn.isSortValueLive()) { // objects that can't change aren't live if (!(valueToSort instanceof Number) && !(valueToSort instanceof String) && !(valueToSort instanceof TableColumnSortObject)) { tableColumn.setSortValueLive(true); } } return _setSortValue(valueToSort); } private boolean _setSortValue(Comparable valueToSort) { checkCellForSetting(); if (sortValue == valueToSort) return false; if (hasFlag(FLAG_SORTVALUEISTEXT)) { clearFlag(FLAG_SORTVALUEISTEXT); if (sortValue instanceof String) // Make sure text is actually in the cell (it may not have been if // cell wasn't created at the time of setting) setText((String)sortValue); } if ((valueToSort instanceof String) && (sortValue instanceof String) && sortValue.equals(valueToSort)) { return false; } if ((valueToSort instanceof Number) && (sortValue instanceof Number) && sortValue.equals(valueToSort)) { return false; } if (bDebug) debug("Setting SortValue to " + ((valueToSort == null) ? "null" : valueToSort.getClass().getName())); tableColumn.setLastSortValueChange(SystemTime.getCurrentTime()); sortValue = valueToSort; // Columns with SWT Paint Listeners usually rely on a repaint whenever the // sort value changes if (cellSWTPaintListeners != null || tableColumn.hasCellOtherListeners("SWTPaint")) { redraw(); } return true; } public boolean setSortValue(long valueToSort) { checkCellForSetting(); if ((sortValue instanceof Long) && ((Long) sortValue).longValue() == valueToSort) return false; return _setSortValue(Long.valueOf(valueToSort)); } public boolean setSortValue( float valueToSort ) { checkCellForSetting(); if (sortValue instanceof Float && ((Float) sortValue).floatValue() == valueToSort) return false; return _setSortValue(new Float(valueToSort)); } public Comparable getSortValue() { if (bDebug) debug("GetSortValue;" + (sortValue == null ? "null" : sortValue.getClass().getName() + ";" + sortValue.toString())); if (sortValue == null) { if (bufferedTableItem != null) return bufferedTableItem.getText(); return ""; } return sortValue; } public void setToolTip(Object tooltip) { oToolTip = tooltip; if (tooltip == null) { setFlag(FLAG_TOOLTIPISAUTO); } else { clearFlag(FLAG_TOOLTIPISAUTO); } } public Object getToolTip() { return oToolTip; } public Object getDefaultToolTip() { return defaultToolTip; } public void setDefaultToolTip(Object tt) { defaultToolTip = tt; } public boolean isDisposed() { return (flags & FLAG_DISPOSED) != 0; } // @see org.gudy.azureus2.plugins.ui.tables.TableCell#getMaxLines() public int getMaxLines() { if (bufferedTableItem == null) { // use 1 in case some plugin borks on div by zero return 1; } return bufferedTableItem.getMaxLines(); } /* Start TYPE_GRAPHIC Functions */ public Point getSize() { if (!(bufferedTableItem instanceof BufferedGraphicTableItem)) return null; return ((BufferedGraphicTableItem)bufferedTableItem).getSize(); } public int getWidth() { if (QUICK_WIDTH) { return tableColumn.getWidth() - 2 - (getMarginWidth() * 2); } Point pt = null; if (bufferedTableItem instanceof BufferedGraphicTableItem) { pt = ((BufferedGraphicTableItem)bufferedTableItem).getSize(); } else { Rectangle bounds = bufferedTableItem.getBounds(); if (bounds != null) { pt = new Point(bounds.width, bounds.height); } } if (pt == null) return -1; return pt.x; } public int getHeight() { Point pt = null; if (bufferedTableItem instanceof BufferedGraphicTableItem) { pt = ((BufferedGraphicTableItem)bufferedTableItem).getSize(); } else { Rectangle bounds = bufferedTableItem.getBounds(); if (bounds != null) { pt = new Point(bounds.width, bounds.height); } } if (pt == null) return -1; return pt.y; } // @see org.gudy.azureus2.ui.swt.views.table.TableCellSWT#setGraphic(org.eclipse.swt.graphics.Image) public boolean setGraphic(Image img) { checkCellForSetting(); if (!(bufferedTableItem instanceof BufferedGraphicTableItem)) return false; graphic = null; boolean b = ((BufferedGraphicTableItem)bufferedTableItem).setGraphic(img); if (b) { setFlag(FLAG_VISUALLY_CHANGED_SINCE_REFRESH); bufferedTableItem.redraw(); } return b; } // @see org.gudy.azureus2.plugins.ui.tables.TableCell#setGraphic(org.gudy.azureus2.plugins.ui.Graphic) public boolean setGraphic(Graphic img) { if (img != null){ checkCellForSetting(); } if (!(bufferedTableItem instanceof BufferedGraphicTableItem)) return false; if (img == graphic && numFastRefreshes >= MAX_REFRESHES) { pluginError("TableCellImpl::setGraphic to same Graphic object. " + "Forcing refresh."); } graphic = img; if (img == null) { boolean b = ((BufferedGraphicTableItem)bufferedTableItem).setGraphic(null); if (b) { setFlag(FLAG_VISUALLY_CHANGED_SINCE_REFRESH); bufferedTableItem.redraw(); } } if (img instanceof GraphicSWT){ Image imgSWT = ((GraphicSWT)img).getImage(); boolean b = ((BufferedGraphicTableItem)bufferedTableItem).setGraphic(imgSWT); if (b) { setFlag(FLAG_VISUALLY_CHANGED_SINCE_REFRESH); bufferedTableItem.redraw(); } } else if (img instanceof UISWTGraphic){ Image imgSWT = ((UISWTGraphic)img).getImage(); boolean b = ((BufferedGraphicTableItem)bufferedTableItem).setGraphic(imgSWT); if (b) { setFlag(FLAG_VISUALLY_CHANGED_SINCE_REFRESH); bufferedTableItem.redraw(); } } return( false ); } public Graphic getGraphic() { if (graphic != null) { return graphic; } if (!(bufferedTableItem instanceof BufferedGraphicTableItem)) return null; Image img = ((BufferedGraphicTableItem)bufferedTableItem).getGraphic(); return new UISWTGraphicImpl(img); } public Image getGraphicSWT() { if (!(bufferedTableItem instanceof BufferedGraphicTableItem)) return null; return ((BufferedGraphicTableItem)bufferedTableItem).getGraphic(); } public void setFillCell(boolean bFillCell) { checkCellForSetting(); if (!(bufferedTableItem instanceof BufferedGraphicTableItem)) return; if (bFillCell) ((BufferedGraphicTableItem)bufferedTableItem).setOrientation(SWT.FILL); else setOrientationViaColumn(); setFlag(FLAG_VISUALLY_CHANGED_SINCE_REFRESH); } public void setMarginHeight(int height) { checkCellForSetting(); if (!(bufferedTableItem instanceof BufferedGraphicTableItem)) return; ((BufferedGraphicTableItem)bufferedTableItem).setMargin(-1, height); setFlag(FLAG_VISUALLY_CHANGED_SINCE_REFRESH); } public void setMarginWidth(int width) { checkCellForSetting(); if (!(bufferedTableItem instanceof BufferedGraphicTableItem)) return; ((BufferedGraphicTableItem)bufferedTableItem).setMargin(width, -1); setFlag(FLAG_VISUALLY_CHANGED_SINCE_REFRESH); } public int getMarginHeight() { if (!(bufferedTableItem instanceof BufferedGraphicTableItem)) return 0; return ((BufferedGraphicTableItem)bufferedTableItem).getMarginHeight(); } public int getMarginWidth() { if (!(bufferedTableItem instanceof BufferedGraphicTableItem)) return 0; return ((BufferedGraphicTableItem)bufferedTableItem).getMarginWidth(); } /* End TYPE_GRAPHIC Functions */ public void addRefreshListener(TableCellRefreshListener listener) { try{ this_mon.enter(); if (refreshListeners == null) refreshListeners = new ArrayList(1); if (bDebug) { debug("addRefreshListener; count=" + refreshListeners.size()); } refreshListeners.add(listener); }finally{ this_mon.exit(); } } public void removeRefreshListener(TableCellRefreshListener listener) { try{ this_mon.enter(); if (refreshListeners == null) return; refreshListeners.remove(listener); }finally{ this_mon.exit(); } } public void addDisposeListener(TableCellDisposeListener listener) { try{ this_mon.enter(); if (disposeListeners == null) { disposeListeners = new ArrayList(1); } disposeListeners.add(listener); }finally{ this_mon.exit(); } } public void removeDisposeListener(TableCellDisposeListener listener) { try{ this_mon.enter(); if (disposeListeners == null) return; disposeListeners.remove(listener); }finally{ this_mon.exit(); } } public void addToolTipListener(TableCellToolTipListener listener) { try{ this_mon.enter(); if (tooltipListeners == null) { tooltipListeners = new ArrayList(1); } tooltipListeners.add(listener); }finally{ this_mon.exit(); } } public void removeToolTipListener(TableCellToolTipListener listener) { try{ this_mon.enter(); if (tooltipListeners == null) return; tooltipListeners.remove(listener); }finally{ this_mon.exit(); } } public void addMouseListener(TableCellMouseListener listener) { try { this_mon.enter(); if (cellMouseListeners == null) cellMouseListeners = new ArrayList(1); cellMouseListeners.add(listener); } finally { this_mon.exit(); } } public void removeMouseListener(TableCellMouseListener listener) { try { this_mon.enter(); if (cellMouseListeners == null) return; cellMouseListeners.remove(listener); } finally { this_mon.exit(); } } public void addMouseMoveListener(TableCellMouseMoveListener listener) { try { this_mon.enter(); if (cellMouseMoveListeners == null) cellMouseMoveListeners = new ArrayList(1); cellMouseMoveListeners.add(listener); } finally { this_mon.exit(); } } public void removeMouseMoveListener(TableCellMouseMoveListener listener) { try { this_mon.enter(); if (cellMouseMoveListeners == null) return; cellMouseMoveListeners.remove(listener); } finally { this_mon.exit(); } } public void addVisibilityListener(TableCellVisibilityListener listener) { try { this_mon.enter(); if (cellVisibilityListeners == null) cellVisibilityListeners = new ArrayList(1); cellVisibilityListeners.add(listener); } finally { this_mon.exit(); } } public void removeVisibilityListener(TableCellVisibilityListener listener) { try { this_mon.enter(); if (cellVisibilityListeners == null) return; cellVisibilityListeners.remove(listener); } finally { this_mon.exit(); } } /** * @param listenerObject * * @since 3.1.1.1 */ private void addSWTPaintListener(TableCellSWTPaintListener listener) { try { this_mon.enter(); if (cellSWTPaintListeners == null) cellSWTPaintListeners = new ArrayList(1); cellSWTPaintListeners.add(listener); } finally { this_mon.exit(); } } public void invokeSWTPaintListeners(GC gc) { if (tableColumn != null) { Object[] swtPaintListeners = tableColumn.getCellOtherListeners("SWTPaint"); if (swtPaintListeners != null) { for (int i = 0; i < swtPaintListeners.length; i++) { try { TableCellSWTPaintListener l = (TableCellSWTPaintListener) swtPaintListeners[i]; l.cellPaint(gc, this); } catch (Throwable e) { Debug.printStackTrace(e); } } } } if (cellSWTPaintListeners == null) { return; } for (int i = 0; i < cellSWTPaintListeners.size(); i++) { try { TableCellSWTPaintListener l = (TableCellSWTPaintListener) (cellSWTPaintListeners.get(i)); l.cellPaint(gc, this); } catch (Throwable e) { Debug.printStackTrace(e); } } } private void addCellClipboardListener(TableCellClipboardListener listener) { try { this_mon.enter(); if (cellClipboardListeners == null) cellClipboardListeners = new ArrayList<TableCellClipboardListener>(1); cellClipboardListeners.add(listener); } finally { this_mon.exit(); } } public String getClipboardText() { String text = tableColumn.getClipboardText(this); if (text != null) { return text; } try { this_mon.enter(); if (cellClipboardListeners != null) { for (TableCellClipboardListener l : cellClipboardListeners) { try { text = l.getClipboardText(this); } catch (Exception e) { Debug.out(e); } if (text != null) { break; } } } } finally { this_mon.exit(); } if (text == null) { text = this.getText(); } return text; } public void addListeners(Object listenerObject) { if (listenerObject instanceof TableCellDisposeListener) { addDisposeListener((TableCellDisposeListener)listenerObject); } if (listenerObject instanceof TableCellRefreshListener) addRefreshListener((TableCellRefreshListener)listenerObject); if (listenerObject instanceof TableCellToolTipListener) addToolTipListener((TableCellToolTipListener)listenerObject); if (listenerObject instanceof TableCellMouseMoveListener) { addMouseMoveListener((TableCellMouseMoveListener) listenerObject); } if (listenerObject instanceof TableCellMouseListener) { addMouseListener((TableCellMouseListener) listenerObject); } if (listenerObject instanceof TableCellVisibilityListener) addVisibilityListener((TableCellVisibilityListener)listenerObject); if (listenerObject instanceof TableCellSWTPaintListener) addSWTPaintListener((TableCellSWTPaintListener)listenerObject); if (listenerObject instanceof TableCellClipboardListener) addCellClipboardListener((TableCellClipboardListener)listenerObject); } /** * If a plugin in trying to invalidate a cell, then clear the sort value * too. */ public void invalidate() { checkCellForSetting(); invalidate(true); } /* Start of Core-Only function */ ////////////////////////////////// public void redraw() { if (!tableRow.isVisible()) { return; } if (bufferedTableItem != null) { bufferedTableItem.redraw(); } } public void invalidate(final boolean bMustRefresh) { if ((flags & FLAG_VALID) == 0) { //!hasFlag(FLAG_VALID) if (bMustRefresh) { if ((flags & FLAG_MUSTREFRESH) != 0) { return; } } else { return; } } clearFlag(FLAG_VALID); setFlag(FLAG_VISUALLY_CHANGED_SINCE_REFRESH); if (bDebug) debug("Invalidate Cell;" + bMustRefresh); if (bMustRefresh) { setFlag(FLAG_MUSTREFRESH); if (bufferedTableItem != null) { bufferedTableItem.invalidate(); } } } // @see com.aelitis.azureus.ui.common.table.TableCellCore#refresh() public boolean refresh() { return refresh(true); } // @see com.aelitis.azureus.ui.common.table.TableCellCore#refreshAsync() public void refreshAsync() { if (bInRefreshAsync) { //System.out.println(System.currentTimeMillis() + "] SKIP " + restartRefresh); if (restartRefresh < Byte.MAX_VALUE) { restartRefresh++; } return; } bInRefreshAsync = true; AERunnable runnable = new AERunnable() { public void runSupport() { //System.out.println(System.currentTimeMillis() + "] REFRESH!"); restartRefresh = 0; refresh(true); bInRefreshAsync = false; //System.out.println(System.currentTimeMillis() + "] REFRESH OUT!"); if (restartRefresh > 0) { refreshAsync(); } } }; Utils.execSWTThreadLater(25, runnable); } // @see com.aelitis.azureus.ui.common.table.TableCellCore#refresh(boolean) public boolean refresh(boolean bDoGraphics) { boolean isRowShown; if (tableRow != null) { TableView view = tableRow.getView(); isRowShown = view.isRowVisible(tableRow); } else { isRowShown = true; } boolean isCellShown = isRowShown && isShown(); return refresh(bDoGraphics, isRowShown, isCellShown); } // @see com.aelitis.azureus.ui.common.table.TableCellCore#refresh(boolean, boolean) public boolean refresh(boolean bDoGraphics, boolean bRowVisible) { boolean isCellShown = bRowVisible && isShown(); return refresh(bDoGraphics, bRowVisible, isCellShown); } // @see com.aelitis.azureus.ui.common.table.TableCellCore#refresh(boolean, boolean, boolean) public boolean refresh(boolean bDoGraphics, boolean bRowVisible, boolean bCellVisible) { if (tableColumn == null) { return false; } boolean ret = getVisuallyChangedSinceRefresh(); clearFlag(FLAG_VISUALLY_CHANGED_SINCE_REFRESH); int iErrCount = 0; if (refreshErrLoopCount > 2) { return ret; } iErrCount = tableColumn.getConsecutiveErrCount(); if (iErrCount > 10) { refreshErrLoopCount = 3; return ret; } if (bInRefresh) { // Skip a Refresh call when being called from within refresh. // This could happen on virtual tables where SetData calls us again, or // if we ever introduce plugins to refresh. if (bDebug) debug("Calling Refresh from Refresh :) Skipping."); return ret; } try { bInRefresh = true; if (ret) { long now = SystemTime.getCurrentTime(); if (now - lastRefresh < MAX_REFRESHES_WITHIN_MS) { numFastRefreshes++; if (numFastRefreshes >= MAX_REFRESHES) { if ((numFastRefreshes % MAX_REFRESHES) == 0) { pluginError("this plugin is crazy. tried to refresh " + numFastRefreshes + " times in " + (now - lastRefresh) + "ms"); } return ret; } } else { numFastRefreshes = 0; lastRefresh = now; } } // See bIsUpToDate variable comments if (bCellVisible && !isUpToDate()) { if (bDebug) debug("Setting Invalid because visible & not up to date"); clearFlag(FLAG_VALID); setFlag(FLAG_UPTODATE); } else if (!bCellVisible && isUpToDate()) { if (bDebug) debug("Setting not up to date because cell not visible " + Debug.getCompressedStackTrace()); clearFlag(FLAG_UPTODATE); } if (bDebug) { debug("Cell Valid?" + hasFlag(FLAG_VALID) + "; Visible?" + tableRow.isVisible() + "/" + bufferedTableItem.isShown()); } int iInterval = tableColumn.getRefreshInterval(); if (iInterval == TableColumnCore.INTERVAL_INVALID_ONLY && !hasFlag(FLAG_MUSTREFRESH | FLAG_VALID) && hasFlag(FLAG_SORTVALUEISTEXT) && sortValue != null && tableColumn.getType() == TableColumnCore.TYPE_TEXT_ONLY) { if (bCellVisible) { if (bDebug) debug("fast refresh: setText"); ret = setText((String)sortValue); setFlag(FLAG_VALID); } } else if ((iInterval == TableColumnCore.INTERVAL_LIVE || (iInterval == TableColumnCore.INTERVAL_GRAPHIC && bDoGraphics) || (iInterval > 0 && (loopFactor % iInterval) == 0) || !hasFlag(FLAG_VALID) || hasFlag(FLAG_MUSTREFRESH))) { boolean bWasValid = isValid(); if (hasFlag(FLAG_MUSTREFRESH)) { clearFlag(FLAG_MUSTREFRESH); } if (bDebug) debug("invoke refresh; wasValid? " + bWasValid); long lTimeStart = Constants.isCVSVersion()?SystemTime.getMonotonousTime():0; tableColumn.invokeCellRefreshListeners(this, !bCellVisible); if (refreshListeners != null) { for (int i = 0; i < refreshListeners.size(); i++) { TableCellRefreshListener l = (TableCellRefreshListener)refreshListeners.get(i); if(l instanceof TableCellLightRefreshListener) ((TableCellLightRefreshListener)l).refresh(this,!bCellVisible); else l.refresh(this); } } if ( Constants.isCVSVersion()){ long lTimeEnd = SystemTime.getMonotonousTime(); tableColumn.addRefreshTime(lTimeEnd - lTimeStart); } // Change to valid only if we weren't valid before the listener calls // This is in case the listeners set valid to false when it was true if (!bWasValid && !hasFlag(FLAG_MUSTREFRESH)) { setFlag(FLAG_VALID); } } loopFactor++; refreshErrLoopCount = 0; if (iErrCount > 0) tableColumn.setConsecutiveErrCount(0); ret = getVisuallyChangedSinceRefresh(); if (bDebug) debug("refresh done; visual change? " + ret + ";" + Debug.getCompressedStackTrace()); } catch (Throwable e) { refreshErrLoopCount++; if (tableColumn != null) { tableColumn.setConsecutiveErrCount(++iErrCount); } pluginError(e); if (refreshErrLoopCount > 2) Logger.log(new LogEvent(LOGID, LogEvent.LT_ERROR, "TableCell will not be refreshed anymore this session.")); } finally { bInRefresh = false; } if (childCells != null) { Object[] childCellsArray = childCells.toArray(); for (int i = 0; i < childCellsArray.length; i++) { TableCellImpl childCell = (TableCellImpl) childCellsArray[i]; childCell.refresh(bDoGraphics, bRowVisible, bCellVisible); } } return ret; } public boolean getVisuallyChangedSinceRefresh() { return hasFlag(FLAG_VISUALLY_CHANGED_SINCE_REFRESH); } public void dispose() { setFlag(FLAG_DISPOSED); tableColumn.invokeCellDisposeListeners(this); if (disposeListeners != null) { try { for (Iterator iter = disposeListeners.iterator(); iter.hasNext();) { TableCellDisposeListener listener = (TableCellDisposeListener)iter.next(); listener.dispose(this); } disposeListeners = null; } catch (Throwable e) { pluginError(e); } } if (bufferedTableItem != null) { //bufferedTableItem.setForeground(null); bufferedTableItem.dispose(); } refreshListeners = null; bufferedTableItem = null; tableColumn = null; tableRow = null; sortValue = null; } public boolean setIcon(Image img) { if (isInvisibleAndCanRefresh()) return false; icon = img; graphic = null; setFlag(FLAG_VISUALLY_CHANGED_SINCE_REFRESH); return true; } public Image getIcon() { return icon; } public boolean needsPainting() { if (cellSWTPaintListeners != null || tableColumn.hasCellOtherListeners("SWTPaint")) { return true; } if (bufferedTableItem == null) { return false; } return bufferedTableItem.needsPainting(); } public void doPaint(GC gc) { //This sometimes causes a infinite loop if the listener invalidates //the drawing area //if ((!hasFlag(FLAG_UPTODATE) || !hasFlag(FLAG_VALID)) && !bInRefresh && !bInRefreshAsync // && (refreshListeners != null || tableColumn.hasCellRefreshListener())) { // if (bDebug) { // debug("doPaint: invoke refresh"); // } // refresh(true); //} if (bDebug) { debug("doPaint up2date:" + hasFlag(FLAG_UPTODATE) + ";v:" + hasFlag(FLAG_VALID) + ";rl=" + refreshListeners); } invokeSWTPaintListeners(gc); bufferedTableItem.doPaint(gc); if (childCells != null) { Object[] childCellsArray = childCells.toArray(); for (int i = 0; i < childCellsArray.length; i++) { TableCellImpl childCell = (TableCellImpl) childCellsArray[i]; childCell.doPaint(gc); } } } public void locationChanged() { if (bufferedTableItem != null) { bufferedTableItem.locationChanged(); } } public TableRowCore getTableRowCore() { return tableRow; } // @see org.gudy.azureus2.ui.swt.views.table.TableCellSWT#getTableRowSWT() public TableRowSWT getTableRowSWT() { if (tableRow instanceof TableRowSWT) { return (TableRowSWT)tableRow; } return null; } /* (non-Javadoc) * @see java.lang.Object#toString() */ public String toString() { return "TableCell {" + tableColumn.getName() + "," + (tableRow == null ? "" : "r" + tableRow.getIndex()) + (bufferedTableItem == null ? "c?" : "c" + bufferedTableItem.getPosition()) + "," + getText() + "," + getSortValue() + "}"; } /* Comparable Implementation */ /** Compare our sortValue to the specified object. Assumes the object * is TableCellImp (safe assumption) */ public int compareTo(Object o) { try { Comparable ourSortValue = getSortValue(); Comparable otherSortValue = ((TableCellImpl)o).getSortValue(); if (ourSortValue instanceof String && otherSortValue instanceof String) { // Collator.getInstance cache's Collator object, so this is relatively // fast. However, storing it as static somewhere might give a small // performance boost. If such an approach is take, ensure that the static // variable is updated the user chooses an different language. Collator collator = Collator.getInstance(Locale.getDefault()); return collator.compare(ourSortValue, otherSortValue); } try { return ourSortValue.compareTo(otherSortValue); } catch (ClassCastException e) { // It's possible that a row was created, but not refreshed yet. // In that case, one sortValue will be String, and the other will be // a comparable object that the plugin defined. Those two sortValues // may not be compatable (for good reason!), so just skip it. } } catch (Exception e) { System.out.println("Could not compare cells"); Debug.printStackTrace( e ); } return 0; } public void invokeToolTipListeners(int type) { if (tableColumn == null) return; tableColumn.invokeCellToolTipListeners(this, type); if (tooltipListeners == null || tooltipErrLoopCount > 2) return; int iErrCount = tableColumn.getConsecutiveErrCount(); if (iErrCount > 10) return; try { if (type == TOOLTIPLISTENER_HOVER) { for (int i = 0; i < tooltipListeners.size(); i++) ((TableCellToolTipListener)(tooltipListeners.get(i))).cellHover(this); } else { for (int i = 0; i < tooltipListeners.size(); i++) ((TableCellToolTipListener)(tooltipListeners.get(i))).cellHoverComplete(this); } tooltipErrLoopCount = 0; } catch (Throwable e) { tooltipErrLoopCount++; tableColumn.setConsecutiveErrCount(++iErrCount); pluginError(e); if (tooltipErrLoopCount > 2) Logger.log(new LogEvent(LOGID, LogEvent.LT_ERROR, "TableCell's tooltip will not be refreshed anymore this session.")); } } public void invokeMouseListeners(TableCellMouseEvent event) { ArrayList listeners = event.eventType == TableCellMouseEvent.EVENT_MOUSEMOVE ? cellMouseMoveListeners : cellMouseListeners; if (listeners == null) return; if (event.cell != null && event.row == null) { event.row = event.cell.getTableRow(); } for (int i = 0; i < listeners.size(); i++) { try { TableCellMouseListener l = (TableCellMouseListener) (listeners.get(i)); l.cellMouseTrigger(event); } catch (Throwable e) { Debug.printStackTrace(e); } } } public void invokeVisibilityListeners(int visibility, boolean invokeColumnListeners) { if (invokeColumnListeners) { tableColumn.invokeCellVisibilityListeners(this, visibility); } if (cellVisibilityListeners == null) return; for (int i = 0; i < cellVisibilityListeners.size(); i++) { try { TableCellVisibilityListener l = (TableCellVisibilityListener) (cellVisibilityListeners.get(i)); l.cellVisibilityChanged(this, visibility); } catch (Throwable e) { Debug.printStackTrace(e); } } } public static final Comparator TEXT_COMPARATOR = new TextComparator(); private static class TextComparator implements Comparator { public int compare(Object arg0, Object arg1) { return arg0.toString().compareToIgnoreCase(arg1.toString()); } } public void setUpToDate(boolean upToDate) { if (bDebug) debug("set up to date to " + upToDate); if (upToDate) { setFlag(FLAG_UPTODATE); } else { clearFlag(FLAG_UPTODATE); } } public boolean isUpToDate() { return hasFlag(FLAG_UPTODATE); } public void debug(final String s) { Utils.execSWTThread(new AERunnable() { public void runSupport() { System.out.println(SystemTime.getCurrentTime() + ": r" + tableRow.getIndex() + "c" + tableColumn.getPosition() + "r.v?" + ((tableRow.isVisible() ? "Y" : "N")) + ";" + s); } }, true); } public Rectangle getBounds() { if (isDisposed()) { return new Rectangle(0, 0, 0, 0); } Rectangle bounds = bufferedTableItem.getBounds(); if (bounds == null) { return new Rectangle(0, 0, 0, 0); } return bounds; } private void setOrientationViaColumn() { if (!(bufferedTableItem instanceof BufferedGraphicTableItem)) return; int align = tableColumn.getAlignment(); BufferedGraphicTableItem ti = (BufferedGraphicTableItem) bufferedTableItem; ti.setOrientation(TableColumnSWTUtils.convertColumnAlignmentToSWT(align)); } public String getObfusticatedText() { if (tableColumn.isObfusticated()) { if (tableColumn instanceof ObfusticateCellText) { return ((ObfusticateCellText)tableColumn).getObfusticatedText(this); } return ""; } return null; } public Graphic getBackgroundGraphic() { if (bufferedTableItem == null) { return null; } return new UISWTGraphicImpl(bufferedTableItem.getBackgroundImage()); } public Image getBackgroundImage() { if (bufferedTableItem == null) { return null; } return bufferedTableItem.getBackgroundImage(); } public BufferedTableItem getBufferedTableItem() { return bufferedTableItem; } public int getCursorID() { return iCursorID; } public void setCursorID(int cursorID) { if (iCursorID == cursorID) { return; } iCursorID = cursorID; Utils.execSWTThread(new AERunnable() { public void runSupport() { if (isMouseOver()) { bufferedTableItem.setCursor(iCursorID); } } }); } public boolean isMouseOver() { if (bufferedTableItem == null) { return false; } if (!tableRow.isVisible()) { return false; } return bufferedTableItem.isMouseOver(); } public int[] getMouseOffset() { Point ofs = ((TableViewSWT) tableRow.getView()).getTableCellMouseOffset(this); return ofs == null ? null : new int[] { ofs.x, ofs.y }; } private boolean hasFlag(int flag) { return (flags & flag) != 0; } private void setFlag(int flag) { flags |= flag; } private void clearFlag(int flag) { flags &= ~flag; } /** * @param childCell * * @since 3.0.5.3 */ public void addChildCell(TableCellImpl childCell) { if (childCells == null) { childCells = new ArrayList(1); } //TODO: childCell.setParentCell(this); childCells.add(childCell); } public int getTextAlpha() { return textAlpha; } public void setTextAlpha(int textOpacity) { this.textAlpha = textOpacity; } public Rectangle getBoundsOnDisplay() { Rectangle bounds = getBounds(); Point pt = ((TableViewSWT) tableRow.getView()).getTableOrTreeSWT().toDisplay(bounds.x, bounds.y); bounds.x = pt.x; bounds.y = pt.y; return bounds; } }