/*
* 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.*;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.widgets.Item;
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.*;
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.*;
import org.gudy.azureus2.ui.swt.views.table.utils.TableColumnSWTUtils;
import com.aelitis.azureus.ui.common.table.*;
/** 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 boolean canUseQuickDraw = Constants.isWindows;
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" );
// Getting the cell's bounds can be slow. QUICK_WIDTH uses TableColumn's width
private static final boolean QUICK_WIDTH = true;
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;
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) {
bufferedTableItem = new BufferedGraphicTableItem1(bufRow, position) {
public void refresh() {
TableCellImpl.this.refresh();
}
public void invalidate() {
clearFlag(FLAG_VALID);
redraw();
}
protected void quickRedrawCell(TableOrTreeSWT table, Rectangle dirty, Rectangle cellBounds) {
TableItemOrTreeItem item = row.getItem();
boolean ourQuickRedraw = canUseQuickDraw && tableRow != null
&& !tableRow.isMouseOver() && !tableRow.isSelected();
if (ourQuickRedraw) {
TableCellImpl.this.quickRedrawCell2(table, item, dirty, cellBounds);
} else {
super.quickRedrawCell(table, dirty, cellBounds);
}
}
};
setOrientationViaColumn();
} else {
bufferedTableItem = new BufferedTableItemImpl(bufRow, position) {
public void refresh() {
TableCellImpl.this.refresh();
}
public void invalidate() {
clearFlag(FLAG_VALID);
}
protected void quickRedrawCell(TableOrTreeSWT table, Rectangle dirty,
Rectangle cellBounds) {
TableItemOrTreeItem item = row.getItem();
boolean ourQuickRedraw = canUseQuickDraw && tableRow != null
&& !tableRow.isMouseOver() && !tableRow.isSelected();
if (ourQuickRedraw) {
TableCellImpl.this.quickRedrawCell2(table, item, dirty, cellBounds);
} else {
super.quickRedrawCell(table, dirty, cellBounds);
}
}
};
}
}
protected void quickRedrawCell2(TableOrTreeSWT table,
TableItemOrTreeItem tableItemOrTreeItem, Rectangle dirty,
Rectangle cellBounds) {
if (bufferedTableItem.isInPaintItem()) {
return;
}
Rectangle bounds = new Rectangle(0, 0, cellBounds.width, cellBounds.height);
Point pt = new Point(cellBounds.x, cellBounds.y);
Image img = new Image(table.getDisplay(), bounds);
int colPos = bufferedTableItem.getPosition();
Item item = tableItemOrTreeItem.getItem();
table.setData("inPaintInfo", new InPaintInfo(item, colPos, bounds));
table.setData("fullPaint", Boolean.TRUE);
GC gc = new GC(img);
try {
TableViewSWTImpl tv = (TableViewSWTImpl) tableRow.getView();
TableViewSWT_EraseItem.eraseItem(null, gc, tableItemOrTreeItem, colPos,
false, bounds, tv, true);
//gc.setBackground(ColorCache.getRandomColor());
//gc.fillRectangle(bounds);
Color fg = getForegroundSWT();
if (fg != null) {
gc.setForeground(fg);
}
gc.setBackground(getBackgroundSWT());
TableViewSWT_PaintItem.paintItem(gc, tableItemOrTreeItem, colPos,
tableRow.getIndex(), bounds, tv, true);
} finally {
gc.dispose();
}
gc = new GC(table.getComposite());
try {
//System.out.println("draw " + bounds);
gc.drawImage(img, pt.x, pt.y);
} finally {
img.dispose();
gc.dispose();
}
table.setData("inPaintInfo", null);
table.setData("fullPaint", Boolean.FALSE);
}
protected void quickRedrawCell(TableOrTreeSWT table,
TableItemOrTreeItem tableItemOrTreeItem, Rectangle dirty,
Rectangle cellBounds) {
if (bufferedTableItem.isInPaintItem()) {
return;
}
int colPos = bufferedTableItem.getPosition();
Item item = tableItemOrTreeItem.getItem();
table.setData("inPaintInfo", new InPaintInfo(item, colPos, cellBounds));
table.setData("fullPaint", Boolean.TRUE);
GC gc = new GC(table.getComposite());
try {
TableViewSWTImpl tv = (TableViewSWTImpl) tableRow.getView();
TableViewSWT_EraseItem.eraseItem(null, gc, tableItemOrTreeItem,
bufferedTableItem.getPosition(), true, cellBounds, tv, true);
Color fg = getForegroundSWT();
if (fg != null) {
gc.setForeground(fg);
}
gc.setBackground(getBackgroundSWT());
TableViewSWT_PaintItem.paintItem(gc, tableItemOrTreeItem,
bufferedTableItem.getPosition(), tableRow.getIndex(), cellBounds, tv, true);
} finally {
gc.dispose();
}
table.setData("inPaintInfo", null);
table.setData("fullPaint", Boolean.FALSE);
}
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 (bInRefresh && Utils.isThisThreadSWT()) {
// System.out.println("Invalidating when in refresh via " + Debug.getCompressedStackTrace());
//}
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);
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;
}
}