package org.gudy.azureus2.ui.swt.views.table.impl;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Locale;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.widgets.Display;
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.tables.*;
import org.gudy.azureus2.ui.swt.Utils;
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 com.aelitis.azureus.ui.common.table.*;
import com.aelitis.azureus.ui.swt.utils.ColorCache;
public abstract class TableCellSWTBase
implements TableCellSWT
{
private static final LogIDs LOGID = LogIDs.GUI;
private static AEMonitor this_mon = new AEMonitor("TableCell");
/**
* Plugins read this to see if their datasource has changed.
* {@link #invalidate()} will clear this flag, causing the cell to set its ui again
*/
protected static final int FLAG_VALID = 1;
/**
* Indicates if the sort value is also the text displayed. We can optimize.
*/
protected static final int FLAG_SORTVALUEISTEXT = 2;
/**
* Indicates if the tooltip is autogenerated
*/
protected static final int 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).
*/
protected static final int FLAG_UPTODATE = 8;
protected static final int FLAG_DISPOSED = 16;
/**
* Cell has been invalidated, it must refresh on next cycle
*/
protected static final int FLAG_MUSTREFRESH = 32;
/**
* If any visuals change between 2 refreshes, this flag gets set
*/
public static final int FLAG_VISUALLY_CHANGED_SINCE_REFRESH = 64;
private static final boolean DEBUGONLYZERO = false;
private static final boolean DEBUG_FLAGS = false;
private int flags;
protected TableRowCore tableRow;
protected TableColumnCore tableColumn;
private byte tooltipErrLoopCount;
public boolean bDebug = false;
protected ArrayList<TableCellRefreshListener> refreshListeners;
private ArrayList<TableCellDisposeListener> disposeListeners;
private ArrayList<TableCellToolTipListener> tooltipListeners;
private ArrayList<TableCellMouseListener> cellMouseListeners;
private ArrayList<TableCellMouseMoveListener> cellMouseMoveListeners;
private ArrayList<TableCellVisibilityListener> cellVisibilityListeners;
protected ArrayList<TableCellSWTPaintListener> cellSWTPaintListeners;
private ArrayList<TableCellClipboardListener> cellClipboardListeners;
protected Comparable sortValue;
private byte restartRefresh = 0;
private boolean bInRefreshAsync = false;
private byte refreshErrLoopCount;
private byte loopFactor;
// TODO: private
protected static int MAX_REFRESHES = 10;
private static int MAX_REFRESHES_WITHIN_MS = 100;
private boolean bInRefresh = false;
private long lastRefresh;
// TODO: Private
protected int numFastRefreshes;
private Object oToolTip;
private Object defaultToolTip;
private int textAlpha = 255;
private boolean doFillCell = false;
private int iCursorID = SWT.CURSOR_ARROW;
private boolean mouseOver;
private Image icon;
private Graphic graphic = null;
public TableCellSWTBase(TableRowCore row, TableColumnCore column) {
flags = FLAG_SORTVALUEISTEXT;
tableRow = row;
tableColumn = column;
tooltipErrLoopCount = 0;
refreshErrLoopCount = 0;
loopFactor = 0;
}
protected void
constructionComplete()
{
// this needs to be done after the subclass has had time to initialise itself
if (tableColumn != null && tableColumn.getType() == TableColumnCore.TYPE_GRAPHIC) {
setMarginHeight(1);
setMarginWidth(1);
}
}
protected abstract void
constructionCompleter();
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() {
if (isDisposed()) {
return "";
}
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);
}
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 != null) {
tableColumn.invokeCellVisibilityListeners(this, visibility);
}
if (cellVisibilityListeners == null)
return;
for (int i = 0; i < cellVisibilityListeners.size(); i++) {
try {
TableCellVisibilityListener l = cellVisibilityListeners.get(i);
l.cellVisibilityChanged(this, visibility);
} catch (Throwable e) {
Debug.printStackTrace(e);
}
}
}
public void dispose() {
if ( isDisposed()){
// parg added this check at same time as removing the isDisposed check in getDataSource
// in case there is some recursive disposal occuring on a dispose-listener
Debug.out( "Double disposal!" );
return;
}
setFlag(FLAG_DISPOSED);
if (tableColumn != null) {
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);
}
}
refreshListeners = null;
tableColumn = null;
tableRow = null;
sortValue = null;
}
public void debug(final String s) {
if (DEBUGONLYZERO && tableColumn.getPosition() != 0) {
return;
}
Utils.execSWTThread(new AERunnable() {
public void runSupport() {
if (tableRow == null) {
System.out.println(SystemTime.getCurrentTime() + ": c"
+ (tableColumn == null ? null : tableColumn.getPosition()) + ";F=" + flagToText(flags, false)
+ ";" + s);
} else {
System.out.println(SystemTime.getCurrentTime() + ": r"
+ tableRow.getIndex() + "c"
+ (tableColumn == null ? null : tableColumn.getPosition())
+ ";r.v?" + ((tableRow.isVisible() ? "Y" : "N")) + "F="
+ flagToText(flags, false) + ";" + s);
}
}
}, true);
}
protected void pluginError(Throwable e) {
if (tableColumn != null) {
String sTitleLanguageKey = tableColumn.getTitleLanguageKey();
String sPosition = tableColumn.getPosition() + " ("
+ MessageText.getString(sTitleLanguageKey) + ")";
Logger.log(new LogEvent(LOGID, "Table Cell Plugin for Column #" + sPosition
+ " generated an exception ", e));
} else {
Logger.log(new LogEvent(LOGID, "Table Cell Plugin generated an exception ", e));
}
}
protected void pluginError(String s) {
String sPosition = "r" + tableRow.getIndex() + "c"
+ tableColumn.getPosition();
Logger.log(new LogEvent(LOGID, LogEvent.LT_ERROR,
"Table Cell Plugin for Column #" + sPosition + ":" + s + "\n "
+ Debug.getStackTrace(true, true)));
}
public boolean refresh() {
return refresh(true);
}
// @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 (Utils.isThisThreadSWT()) {
// System.out.println("ONSWT: " + Debug.getCompressedStackTrace());
// }
TableColumnCore tc = tableColumn;
if (tc == null) {
return false;
}
boolean ret = getVisuallyChangedSinceRefresh();
// don't clear flag -- since anyone can call refresh(), only the code that
// actually refreshes the display should clear the flag.
//clearFlag(FLAG_VISUALLY_CHANGED_SINCE_REFRESH);
int iErrCount = 0;
if (refreshErrLoopCount > 2) {
return ret;
}
iErrCount = tc.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() + "/" + isShown());
}
int iInterval = tc.getRefreshInterval();
if (iInterval == TableColumnCore.INTERVAL_INVALID_ONLY
&& !hasFlag(FLAG_MUSTREFRESH | FLAG_VALID)
&& hasFlag(FLAG_SORTVALUEISTEXT) && sortValue != null
&& tc.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();
ret = hasFlag(FLAG_MUSTREFRESH);
if (ret) {
clearFlag(FLAG_MUSTREFRESH);
}
if (bDebug) {
debug("invoke refresh; wasValid? " + bWasValid);
}
long lTimeStart = Constants.isCVSVersion()
? SystemTime.getMonotonousTime() : 0;
tc.invokeCellRefreshListeners(this, !bCellVisible);
if (refreshListeners != null) {
for (TableCellRefreshListener l : refreshListeners) {
if (l instanceof TableCellLightRefreshListener) {
((TableCellLightRefreshListener) l).refresh(this, !bCellVisible);
} else {
l.refresh(this);
}
}
}
if (Constants.isCVSVersion()) {
long lTimeEnd = SystemTime.getMonotonousTime();
tc.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);
}
} else {
if (bCellVisible && bDebug) {
debug("Skipped refresh; Interval=" + iInterval);
}
}
loopFactor++;
refreshErrLoopCount = 0;
if (iErrCount > 0)
tc.setConsecutiveErrCount(0);
// has changed if visually changed or "must refresh"
ret |= getVisuallyChangedSinceRefresh();
if (bDebug)
debug("refresh done; visual change? " + ret + ";"
+ Debug.getCompressedStackTrace());
} catch (Throwable e) {
refreshErrLoopCount++;
if (tc != null) {
tc.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;
}
return ret;
}
public boolean setSortValue(Comparable valueToSort) {
if ( tableColumn == null ){
return( false );
}
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) {
if (isDisposed()) {
return false;
}
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")) {
setFlag(FLAG_VISUALLY_CHANGED_SINCE_REFRESH);
//redraw();
}
return true;
}
public boolean setSortValue(long valueToSort) {
if (isDisposed()) {
return false;
}
if ((sortValue instanceof Long)
&& ((Long) sortValue).longValue() == valueToSort)
return false;
return _setSortValue(Long.valueOf(valueToSort));
}
public boolean setSortValue(float valueToSort) {
if (isDisposed()) {
return false;
}
if (sortValue instanceof Float
&& ((Float) sortValue).floatValue() == valueToSort)
return false;
return _setSortValue(new Float(valueToSort));
}
public Comparable getSortValue() {
return sortValue;
}
public boolean isValid() {
// Called often.. inline faster
return (flags & FLAG_VALID) != 0;
//return hasFlag(FLAG_VALID);
}
public boolean isDisposed() {
return (flags & FLAG_DISPOSED) != 0;
}
public boolean hasFlag(int flag) {
return (flags & flag) != 0;
}
public void setFlag(int flag) {
if (DEBUG_FLAGS && (flags & flag) != flag) {
debug("SET FLAG " + flagToText((~flags) & flag, true) + " via "
+ Debug.getStackTrace(true, true, 1, 7));
}
flags |= flag;
}
public void clearFlag(int flag) {
if (DEBUG_FLAGS && (flags & flag) != 0) {
debug("CLEAR FLAG " + flagToText(flags & flag, true) + " via "
+ Debug.getStackTrace(true, true, 1, 7));
}
flags &= ~flag;
}
/**
* If a plugin in trying to invalidate a cell, then clear the sort value
* too.
*/
public void invalidate() {
if (isDisposed()) {
return;
}
invalidate(true);
}
/* (non-Javadoc)
* @see com.aelitis.azureus.ui.common.table.TableCellCore#invalidate(boolean)
*/
public void invalidate(boolean bMustRefresh) {
//if (bInRefresh && Utils.isThisThreadSWT()) {
// System.out.println("Invalidating when in refresh via " + Debug.getCompressedStackTrace());
//}
if ((flags & (FLAG_VALID | FLAG_VISUALLY_CHANGED_SINCE_REFRESH)) == FLAG_VISUALLY_CHANGED_SINCE_REFRESH) { //!hasFlag(FLAG_VALID)
if (bMustRefresh) {
if ((flags & FLAG_MUSTREFRESH) != 0) {
return;
}
} else {
if (DEBUG_FLAGS) {
debug("ALREADY FLAGGED for invalidate via " + Debug.getCompressedStackTrace(7));
}
return;
}
}
clearFlag(FLAG_VALID);
if (bDebug)
debug("Invalidate Cell;" + bMustRefresh);
if (bMustRefresh) {
setFlag(FLAG_MUSTREFRESH | FLAG_VISUALLY_CHANGED_SINCE_REFRESH);
} else {
setFlag(FLAG_VISUALLY_CHANGED_SINCE_REFRESH);
}
}
// @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);
}
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 boolean getVisuallyChangedSinceRefresh() {
return hasFlag(FLAG_VISUALLY_CHANGED_SINCE_REFRESH);
}
public void clearVisuallyChangedSinceRefresh() {
clearFlag(FLAG_VISUALLY_CHANGED_SINCE_REFRESH);
}
/** Compare our sortValue to the specified object. Assumes the object
* is TableCellSWTBase (safe assumption)
*/
public int compareTo(Object o) {
try {
Comparable ourSortValue = getSortValue();
Comparable otherSortValue = ((TableCellSWTBase) 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 boolean needsPainting() {
if (isDisposed()) {
return false;
}
if (cellSWTPaintListeners != null
|| tableColumn.hasCellOtherListeners("SWTPaint")) {
return true;
}
return getGraphic() != null;
}
public boolean setText(String text) {
if (isDisposed()) {
return false;
}
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 (uiSetText(text) && !hasFlag(FLAG_SORTVALUEISTEXT))
bChanged = true;
if (bDebug) {
debug("setText (" + bChanged + ") : " + text);
}
if (bChanged) {
setFlag(FLAG_VISUALLY_CHANGED_SINCE_REFRESH);
}
boolean do_auto = tableColumn == null ? false : 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;
}
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 abstract boolean uiSetText(String text);
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);
}
public int getTextAlpha() {
return textAlpha;
}
public void setTextAlpha(int textOpacity) {
this.textAlpha = textOpacity;
}
// @see org.gudy.azureus2.ui.swt.views.table.TableCellSWT#getTableRowSWT()
public TableRowSWT getTableRowSWT() {
if (tableRow instanceof TableRowSWT) {
return (TableRowSWT)tableRow;
}
return null;
}
public TableRowCore getTableRowCore() {
return tableRow;
}
private String flagToText(int flag, boolean onlySet) {
StringBuilder sb = new StringBuilder();
sb.append((flag & FLAG_DISPOSED) > 0 ? 'D' : onlySet ? ' ' : 'd');
sb.append((flag & FLAG_MUSTREFRESH) > 0 ? 'M' : onlySet ? ' ' : 'm');
sb.append((flag & FLAG_SORTVALUEISTEXT) > 0 ? 'S' : onlySet ? ' ' : 's');
sb.append((flag & FLAG_TOOLTIPISAUTO) > 0 ? 'T' : onlySet ? ' ' : 't');
sb.append((flag & FLAG_UPTODATE) > 0 ? 'U' : onlySet ? ' ' : 'u');
sb.append((flag & FLAG_VALID) > 0 ? 'V' : onlySet ? ' ' : 'v');
sb.append((flag & FLAG_VISUALLY_CHANGED_SINCE_REFRESH) > 0 ? "VC" : onlySet ? ' ' : "vc");
return sb.toString();
}
public abstract int getWidthRaw();
/* (non-Javadoc)
* @see org.gudy.azureus2.plugins.ui.tables.TableCell#setFillCell(boolean)
*/
public void setFillCell(boolean doFillCell) {
this.doFillCell = doFillCell;
}
public boolean getFillCell() {
return doFillCell;
}
public TableColumnCore getTableColumnCore() {
return tableColumn;
}
public boolean setCursorID(int cursorID) {
if (iCursorID == cursorID) {
return false;
}
iCursorID = cursorID;
return true;
}
public int getCursorID() {
return iCursorID;
}
public void setMouseOver(boolean b) {
mouseOver = b;
}
public boolean isMouseOver() {
if (tableRow != null && !tableRow.isVisible()) {
// XXX: Should trigger event and set mouseOver false?
return false;
}
return mouseOver;
}
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;
}
// @see org.gudy.azureus2.ui.swt.views.table.TableCellSWT#setGraphic(org.eclipse.swt.graphics.Image)
public boolean setGraphic(Image img) {
return setGraphic(new UISWTGraphicImpl(img));
}
// @see org.gudy.azureus2.plugins.ui.tables.TableCell#setGraphic(org.gudy.azureus2.plugins.ui.Graphic)
public boolean setGraphic(Graphic img) {
if (img != null && isDisposed()) {
return false;
}
if (tableColumn == null
|| tableColumn.getType() != TableColumnCore.TYPE_GRAPHIC) {
return false;
}
if (img == graphic && numFastRefreshes >= MAX_REFRESHES) {
pluginError("TableCellImpl::setGraphic to same Graphic object. "
+ "Forcing refresh.");
}
boolean changed = (img == graphic || (img != null && !img.equals(graphic)) || (graphic != null && !graphic.equals(img)));
graphic = img;
if (changed) {
setFlag(FLAG_VISUALLY_CHANGED_SINCE_REFRESH);
redraw();
}
return changed;
}
public Graphic getGraphic() {
return graphic;
}
public Image getGraphicSWT() {
return (graphic instanceof UISWTGraphic)
? ((UISWTGraphic) graphic).getImage() : null;
}
public boolean isInvisibleAndCanRefresh() {
return !isDisposed() && !isShown()
&& (refreshListeners != null || tableColumn.hasCellRefreshListener());
}
public int[] getBackground() {
Color color = getBackgroundSWT();
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() {
Color color = getForegroundSWT();
if (color == null) {
return new int[3];
}
return new int[] { color.getRed(), color.getGreen(), color.getBlue()
};
}
/* (non-Javadoc)
* @see org.gudy.azureus2.plugins.ui.tables.TableCell#setForeground(int, int, int)
*/
public boolean setForeground(int red, int green, int blue) {
// Don't need to set when not visible
if (isInvisibleAndCanRefresh()) {
return false;
}
if (red < 0 || green < 0 || blue < 0) {
return setForeground((Color) null);
}
return setForeground(new RGB(red, green, blue));
}
private boolean setForeground(final RGB rgb) {
Color colorFG = getForegroundSWT();
boolean changed = colorFG == null || colorFG.isDisposed()
|| !colorFG.getRGB().equals(rgb);
if (changed) {
Utils.execSWTThread(new AERunnable() {
public void runSupport() {
setForeground(ColorCache.getColor(Display.getCurrent(), rgb));
}
});
}
return changed;
}
// @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 int[] getMouseOffset() {
Point ofs = ((TableViewSWT) tableRow.getView()).getTableCellMouseOffset(this);
return ofs == null ? null : new int[] { ofs.x, ofs.y };
}
public String getObfusticatedText() {
if (isDisposed()) {
return null;
}
if (tableColumn.isObfusticated()) {
if (tableColumn instanceof ObfusticateCellText) {
return ((ObfusticateCellText)tableColumn).getObfusticatedText(this);
}
return "";
}
return null;
}
}