/*
* File : Utils.java
* Created : 25 sept. 2003 16:15:07
* By : Olivier
*
* Azureus - a Java Bittorrent client
*
* 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
*/
package org.gudy.azureus2.ui.swt;
import java.io.File;
import java.util.*;
import java.util.List;
import org.eclipse.swt.SWT;
import org.eclipse.swt.SWTException;
import org.eclipse.swt.browser.Browser;
import org.eclipse.swt.dnd.*;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.program.Program;
import org.eclipse.swt.widgets.*;
import org.gudy.azureus2.core3.config.COConfigurationManager;
import org.gudy.azureus2.core3.config.ParameterListener;
import org.gudy.azureus2.core3.disk.DiskManagerFileInfo;
import org.gudy.azureus2.core3.internat.MessageText;
import org.gudy.azureus2.core3.util.*;
import org.gudy.azureus2.core3.util.Timer;
import org.gudy.azureus2.platform.PlatformManager;
import org.gudy.azureus2.platform.PlatformManagerCapabilities;
import org.gudy.azureus2.platform.PlatformManagerFactory;
import org.gudy.azureus2.plugins.platform.PlatformManagerException;
import org.gudy.azureus2.ui.swt.mainwindow.Colors;
import org.gudy.azureus2.ui.swt.mainwindow.SWTThread;
import org.gudy.azureus2.ui.swt.mainwindow.TorrentOpener;
import org.gudy.azureus2.ui.swt.shells.MessageBoxShell;
import org.gudy.azureus2.ui.swt.views.table.*;
import org.gudy.azureus2.ui.swt.views.table.impl.TableDelegate;
import org.gudy.azureus2.ui.swt.views.table.impl.TableOrTreeUtils;
import com.aelitis.azureus.core.util.LaunchManager;
import com.aelitis.azureus.ui.swt.UIFunctionsManagerSWT;
import com.aelitis.azureus.ui.swt.UIFunctionsSWT;
import com.aelitis.azureus.ui.swt.imageloader.ImageLoader;
/**
* @author Olivier
*
*/
public class Utils
{
public static final String GOOD_STRING = "(/|,jI~`gy";
public static final boolean isGTK = SWT.getPlatform().equals("gtk");
public static final boolean isCarbon = SWT.getPlatform().equals("carbon");
public static final boolean isCocoa = SWT.getPlatform().equals("cocoa");
/** Some platforms expand the last column to fit the remaining width of
* the table.
*/
public static final boolean LAST_TABLECOLUMN_EXPANDS = isGTK;
/** GTK already handles alternating background for tables */
public static final boolean TABLE_GRIDLINE_IS_ALTERNATING_COLOR = isGTK || isCocoa;
/**
* Debug/Diagnose SWT exec calls. Provides usefull information like how
* many we are queuing up, and how long each call takes. Good to turn on
* occassionally to see if we coded something stupid.
*/
private static final boolean DEBUG_SWTEXEC = System.getProperty(
"debug.swtexec", "0").equals("1");
private static ArrayList<Runnable> queue;
private static AEDiagnosticsLogger diag_logger;
private static Image[] shellIcons = null;
private static Image icon128;
private final static String[] shellIconNames = {
"azureus",
"azureus32",
"azureus64",
"azureus128"
};
public static final Rectangle EMPTY_RECT = new Rectangle(0, 0, 0, 0);
private static int userMode;
private static boolean isAZ2;
static {
if (DEBUG_SWTEXEC) {
System.out.println("==== debug.swtexec=1, performance may be affected ====");
queue = new ArrayList<Runnable>();
diag_logger = AEDiagnostics.getLogger("swt");
diag_logger.log("\n\nSWT Logging Starts");
AEDiagnostics.addEvidenceGenerator(new AEDiagnosticsEvidenceGenerator(){
public void generate(IndentWriter writer) {
writer.println("SWT Queue:");
writer.indent();
for (Runnable r : queue) {
writer.println(r.toString());
}
writer.exdent();
}
});
} else {
queue = null;
diag_logger = null;
}
COConfigurationManager.addAndFireParameterListener("User Mode", new ParameterListener() {
public void parameterChanged(String parameterName) {
userMode = COConfigurationManager.getIntParameter("User Mode");
}
});
COConfigurationManager.addAndFireParameterListener("ui", new ParameterListener() {
public void parameterChanged(String parameterName) {
isAZ2 = "az2".equals(COConfigurationManager.getStringParameter("ui"));
}
});
}
public static boolean isAZ2UI() {
return isAZ2;
}
public static void disposeComposite(Composite composite, boolean disposeSelf) {
if (composite == null || composite.isDisposed())
return;
Control[] controls = composite.getChildren();
for (int i = 0; i < controls.length; i++) {
Control control = controls[i];
if (control != null && !control.isDisposed()) {
if (control instanceof Composite) {
disposeComposite((Composite) control, true);
}
try {
control.dispose();
} catch (SWTException e) {
Debug.printStackTrace(e);
}
}
}
// It's possible that the composite was destroyed by the child
if (!composite.isDisposed() && disposeSelf)
try {
composite.dispose();
} catch (SWTException e) {
Debug.printStackTrace(e);
}
}
public static void disposeComposite(Composite composite) {
disposeComposite(composite, true);
}
/**
* Dispose of a list of SWT objects
*
* @param disposeList
*/
public static void disposeSWTObjects(List disposeList) {
disposeSWTObjects(disposeList.toArray());
disposeList.clear();
}
public static void disposeSWTObjects(Object[] disposeList) {
if (disposeList == null) {
return;
}
for (int i = 0; i < disposeList.length; i++) {
try {
Object o = disposeList[i];
if (o instanceof Widget && !((Widget) o).isDisposed())
((Widget) o).dispose();
else if ((o instanceof Resource) && !((Resource) o).isDisposed()) {
((Resource) o).dispose();
}
} catch (Exception e) {
Debug.out("Warning: Disposal failed "
+ Debug.getCompressedStackTrace(e, 0, -1, true));
}
}
}
/**
* Initializes the URL dialog with http://
* If a valid link is found in the clipboard, it will be inserted
* and the size (and location) of the dialog is adjusted.
* @param shell to set the dialog location if needed
* @param url the URL text control
* @param accept_magnets
*
* @author Rene Leonhardt
*/
public static void setTextLinkFromClipboard(final Shell shell,
final Text url, boolean accept_magnets) {
String link = getLinkFromClipboard(shell.getDisplay(), accept_magnets);
if (link != null)
url.setText(link);
}
/**
* <p>Gets an URL from the clipboard if a valid URL for downloading has been copied.</p>
* <p>The supported protocols currently are http, https, and magnet.</p>
* @param display
* @param accept_magnets
* @return first valid link from clipboard, else "http://"
*/
public static String getLinkFromClipboard(Display display,
boolean accept_magnets) {
final Clipboard cb = new Clipboard(display);
final TextTransfer transfer = TextTransfer.getInstance();
String data = (String) cb.getContents(transfer);
String text = UrlUtils.parseTextForURL(data, accept_magnets);
if (text == null) {
return "http://";
}
return text;
}
public static void centreWindow(Shell shell) {
Rectangle displayArea; // area to center in
if (shell.getParent() != null) {
displayArea = shell.getParent().getBounds();
} else {
try {
displayArea = shell.getMonitor().getClientArea();
} catch (NoSuchMethodError e) {
displayArea = shell.getDisplay().getClientArea();
}
}
Rectangle shellRect = shell.getBounds();
if (shellRect.height > displayArea.height) {
shellRect.height = displayArea.height;
}
if (shellRect.width > displayArea.width - 50) {
shellRect.width = displayArea.width;
}
shellRect.x = displayArea.x + (displayArea.width - shellRect.width) / 2;
shellRect.y = displayArea.y + (displayArea.height - shellRect.height) / 2;
shell.setBounds(shellRect);
}
/**
* Centers a window relative to a control. That is to say, the window will be located at the center of the control.
* @param window
* @param control
*/
public static void centerWindowRelativeTo(final Shell window,
final Control control) {
final Rectangle bounds = control.getBounds();
final Point shellSize = window.getSize();
window.setLocation(bounds.x + (bounds.width / 2) - shellSize.x / 2,
bounds.y + (bounds.height / 2) - shellSize.y / 2);
}
public static void createTorrentDropTarget(Composite composite,
boolean bAllowShareAdd) {
try {
createDropTarget(composite, bAllowShareAdd, null);
} catch (Exception e) {
Debug.out(e);
}
}
/**
* @param control the control (usually a Shell) to add the DropTarget
* @param url the Text control where to set the link text
*
* @author Rene Leonhardt
*/
public static void createURLDropTarget(Composite composite, Text url) {
try {
createDropTarget(composite, false, url);
} catch (Exception e) {
Debug.out(e);
}
}
private static void createDropTarget(Composite composite,
final boolean bAllowShareAdd, final Text url,
DropTargetListener dropTargetListener) {
Transfer[] transferList = new Transfer[] {
HTMLTransfer.getInstance(),
URLTransfer.getInstance(),
FileTransfer.getInstance(),
TextTransfer.getInstance()
};
final DropTarget dropTarget = new DropTarget(composite, DND.DROP_DEFAULT
| DND.DROP_MOVE | DND.DROP_COPY | DND.DROP_LINK | DND.DROP_TARGET_MOVE);
dropTarget.setTransfer(transferList);
dropTarget.addDropListener(dropTargetListener);
// Note: DropTarget will dipose when the parent it's on diposes
// On Windows, dropping on children moves up to parent
// On OSX, each child needs it's own drop.
if (Constants.isWindows)
return;
Control[] children = composite.getChildren();
for (int i = 0; i < children.length; i++) {
Control control = children[i];
if (!control.isDisposed()) {
if (control instanceof Composite) {
createDropTarget((Composite) control, bAllowShareAdd, url,
dropTargetListener);
} else {
final DropTarget dropTarget2 = new DropTarget(control,
DND.DROP_DEFAULT | DND.DROP_MOVE | DND.DROP_COPY | DND.DROP_LINK
| DND.DROP_TARGET_MOVE);
dropTarget2.setTransfer(transferList);
dropTarget2.addDropListener(dropTargetListener);
}
}
}
}
private static void createDropTarget(Composite composite,
boolean bAllowShareAdd, Text url) {
URLDropTarget target = new URLDropTarget(url, bAllowShareAdd);
createDropTarget(composite, bAllowShareAdd, url, target);
}
private static class URLDropTarget
extends DropTargetAdapter
{
private final Text url;
private final boolean bAllowShareAdd;
public URLDropTarget(Text url, boolean bAllowShareAdd) {
this.url = url;
this.bAllowShareAdd = bAllowShareAdd;
}
public void dropAccept(DropTargetEvent event) {
event.currentDataType = URLTransfer.pickBestType(event.dataTypes,
event.currentDataType);
}
public void dragOver(DropTargetEvent event) {
// skip setting detail if user is forcing a drop type (ex. via the
// ctrl key), providing that the operation is valid
if (event.detail != DND.DROP_DEFAULT
&& ((event.operations & event.detail) > 0))
return;
if ((event.operations & DND.DROP_LINK) > 0)
event.detail = DND.DROP_LINK;
else if ((event.operations & DND.DROP_DEFAULT) > 0)
event.detail = DND.DROP_DEFAULT;
else if ((event.operations & DND.DROP_COPY) > 0)
event.detail = DND.DROP_COPY;
}
public void drop(DropTargetEvent event) {
if (url == null || url.isDisposed()) {
TorrentOpener.openDroppedTorrents(event, bAllowShareAdd);
} else {
if (event.data instanceof URLTransfer.URLType) {
if (((URLTransfer.URLType) event.data).linkURL != null)
url.setText(((URLTransfer.URLType) event.data).linkURL);
} else if (event.data instanceof String) {
String sURL = UrlUtils.parseTextForURL((String) event.data, true);
if (sURL != null) {
url.setText(sURL);
}
}
}
}
}
public static void alternateRowBackground(TableItem item) {
alternateRowBackground(TableOrTreeUtils.getEventItem(item));
}
public static void alternateRowBackground(TableItemOrTreeItem item) {
if (Utils.TABLE_GRIDLINE_IS_ALTERNATING_COLOR) {
if (!item.getParent().getLinesVisible())
item.getParent().setLinesVisible(true);
return;
}
if (item == null || item.isDisposed())
return;
Color[] colors = {
item.getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND),
Colors.colorAltRow
};
Color newColor = colors[item.getParent().indexOf(item) % colors.length];
if (!item.getBackground().equals(newColor)) {
item.setBackground(newColor);
}
}
// Yes, this is actually used by the RSSFeed plugin...
// so don't make private until this is fixed
public static void alternateTableBackground(Table table) {
if (table == null || table.isDisposed())
return;
if (Utils.TABLE_GRIDLINE_IS_ALTERNATING_COLOR) {
if (!table.getLinesVisible())
table.setLinesVisible(true);
return;
}
int iTopIndex = table.getTopIndex();
if (iTopIndex < 0 || (iTopIndex == 0 && table.getItemCount() == 0)) {
return;
}
TableOrTreeSWT tt = TableOrTreeUtils.getTableOrTreeSWT(table);
int iBottomIndex = getTableBottomIndex(tt, iTopIndex);
Color[] colors = {
table.getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND),
Colors.colorAltRow
};
int iFixedIndex = iTopIndex;
for (int i = iTopIndex; i <= iBottomIndex; i++) {
TableItemOrTreeItem row = tt.getItem(i);
// Rows can be disposed!
if (!row.isDisposed()) {
Color newColor = colors[iFixedIndex % colors.length];
iFixedIndex++;
if (!row.getBackground().equals(newColor)) {
// System.out.println("setting "+rows[i].getBackground() +" to " + newColor);
row.setBackground(newColor);
}
}
}
}
/**
* <p>
* Set a MenuItem's image with the given ImageRepository key. In compliance with platform
* human interface guidelines, the images are not set under Mac OS X.
* </p>
* @param item SWT MenuItem
* @param repoKey ImageRepository image key
* @see <a href="http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGMenus/chapter_7_section_3.html#//apple_ref/doc/uid/TP30000356/TPXREF116">Apple HIG</a>
*/
public static void setMenuItemImage(final MenuItem item, final String repoKey) {
if (Constants.isOSX || repoKey == null) {
return;
}
ImageLoader imageLoader = ImageLoader.getInstance();
item.setImage(imageLoader.getImage(repoKey));
item.addDisposeListener(new DisposeListener() {
public void widgetDisposed(DisposeEvent e) {
ImageLoader imageLoader = ImageLoader.getInstance();
imageLoader.releaseImage(repoKey);
}
});
}
public static void setMenuItemImage(final MenuItem item, final Image image) {
if (!Constants.isOSX)
item.setImage(image);
}
/**
* Sets the shell's Icon(s) to the default Azureus icon. OSX doesn't require
* an icon, so they are skipped
*
* @param shell
*/
public static void setShellIcon(Shell shell) {
if (Constants.isOSX) {
if (true) {
return;
}
if (icon128 == null) {
ImageLoader imageLoader = ImageLoader.getInstance();
icon128 = imageLoader.getImage("azureus128");
if (Constants.isCVSVersion()) {
final int border = 9;
Image image = Utils.createAlphaImage(shell.getDisplay(),
128 + (border * 2), 128 + (border * 2));
image = blitImage(shell.getDisplay(), icon128, null, image, new Point(border,
border + 1));
imageLoader.releaseImage("azureus128");
icon128 = image;
// GC gc = new GC(icon128);
// gc.setTextAntialias(SWT.ON);
// gc.setForeground(shell.getDisplay().getSystemColor(SWT.COLOR_YELLOW));
// Font font = getFontWithHeight(gc.getFont(), gc, 20, SWT.BOLD);
// gc.setFont(font);
// GCStringPrinter.printString(gc, Constants.AZUREUS_VERSION,
// new Rectangle(0, 0, 128, 128), false, false, SWT.CENTER
// | SWT.BOTTOM);
// gc.dispose();
// font.dispose();
}
}
shell.setImage(icon128);
return;
}
try {
if (shellIcons == null) {
ArrayList<Image> listShellIcons = new ArrayList<Image>(
shellIconNames.length);
ImageLoader imageLoader = ImageLoader.getInstance();
for (int i = 0; i < shellIconNames.length; i++) {
// never release images since they are always used and stored
// in an array
Image image = imageLoader.getImage(shellIconNames[i]);
if (ImageLoader.isRealImage(image)) {
listShellIcons.add(image);
}
}
shellIcons = (Image[]) listShellIcons.toArray(new Image[listShellIcons.size()]);
}
shell.setImages(shellIcons);
} catch (NoSuchMethodError e) {
// SWT < 3.0
}
}
public static Display getDisplay() {
SWTThread swt = SWTThread.getInstance();
Display display;
if (swt == null) {
display = Display.getDefault();
if (display == null) {
System.err.println("SWT Thread not started yet!");
return null;
}
} else {
if (swt.isTerminated()) {
return null;
}
display = swt.getDisplay();
}
if (display == null || display.isDisposed()) {
return null;
}
return display;
}
/**
* Execute code in the Runnable object using SWT's thread. If current
* thread it already SWT's thread, the code will run immediately. If the
* current thread is not SWT's, code will be run either synchronously or
* asynchronously on SWT's thread at the next reasonable opportunity.
*
* This method does not catch any exceptions.
*
* @param code code to run
* @param async true if SWT asyncExec, false if SWT syncExec
* @return success
*/
public static boolean execSWTThread(final Runnable code, boolean async) {
return execSWTThread(code, async ? -1 : -2);
}
/**
* Schedule execution of the code in the Runnable object using SWT's thread.
* Even if the current thread is the SWT Thread, the code will be scheduled.
* <p>
* Much like Display.asyncExec, except getting the display is handled for you,
* and provides the ability to diagnose and monitor scheduled code run.
*
* @param msLater time to wait before running code on SWT thread. 0 does not
* mean immediate, but as soon as possible.
* @param code Code to run
* @return sucess
*
* @since 3.0.4.3
*/
public static boolean execSWTThreadLater(int msLater, final Runnable code) {
return execSWTThread(code, msLater);
}
/**
*
* @param code
* @param msLater -2: sync<BR>
* -1: sync if on SWT thread, async otherwise<BR>
* 0: async<BR>
* >0: timerExec
* @return
*
* @since 3.0.4.3
*/
public static boolean
isSWTThread()
{
final Display display = getDisplay();
if (display == null ){
return false;
}
return( display.getThread() == Thread.currentThread());
}
private static boolean execSWTThread(final Runnable code, final int msLater) {
final Display display = getDisplay();
if (display == null || code == null) {
return false;
}
boolean isSWTThread = display.getThread() == Thread.currentThread();
if (msLater < 0 && isSWTThread) {
if (queue == null) {
code.run();
} else {
long lStartTimeRun = SystemTime.getCurrentTime();
code.run();
long wait = SystemTime.getCurrentTime() - lStartTimeRun;
if (wait > 700) {
diag_logger.log(SystemTime.getCurrentTime() + "] took " + wait
+ "ms to run " + Debug.getCompressedStackTrace(-5));
}
}
} else if (msLater >= -1) {
try {
if (queue == null) {
if (msLater <= 0) {
display.asyncExec(code);
} else {
if(isSWTThread) {
display.timerExec(msLater, code);
} else {
SimpleTimer.addEvent("execSWTThreadLater",
SystemTime.getOffsetTime(msLater), new TimerEventPerformer() {
public void perform(TimerEvent event) {
if (!display.isDisposed()) {
display.asyncExec(code);
}
}
});
}
}
} else {
queue.add(code);
diag_logger.log(SystemTime.getCurrentTime() + "] + Q. size= "
+ queue.size() + "; add " + code + " via "
+ Debug.getCompressedStackTrace(-5));
final long lStart = SystemTime.getCurrentTime();
final Display fDisplay = display;
final AERunnable runnableWrapper = new AERunnable() {
public void runSupport() {
long wait = SystemTime.getCurrentTime() - lStart - msLater;
if (wait > 700) {
diag_logger.log(SystemTime.getCurrentTime() + "] took " + wait
+ "ms before SWT ran async code " + code);
}
long lStartTimeRun = SystemTime.getCurrentTime();
try {
if (fDisplay.isDisposed()) {
Debug.out("Display disposed while trying to execSWTThread "
+ code);
// run anayway, except trap SWT error
try {
code.run();
} catch (SWTException e) {
Debug.out("Error while execSWTThread w/disposed Display", e);
}
} else {
code.run();
}
} finally {
long runTIme = SystemTime.getCurrentTime() - lStartTimeRun;
if (runTIme > 500) {
diag_logger.log(SystemTime.getCurrentTime() + "] took "
+ runTIme + "ms to run " + code);
}
queue.remove(code);
if (runTIme > 10) {
diag_logger.log(SystemTime.getCurrentTime()
+ "] - Q. size=" + queue.size() + ";wait:" + wait
+ "ms;run:" + runTIme + "ms " + code);
} else {
diag_logger.log(SystemTime.getCurrentTime()
+ "] - Q. size=" + queue.size() + ";wait:" + wait
+ "ms;run:" + runTIme + "ms");
}
}
}
};
if (msLater <= 0) {
display.asyncExec(runnableWrapper);
} else {
if(isSWTThread) {
display.timerExec(msLater, runnableWrapper);
} else {
SimpleTimer.addEvent("execSWTThreadLater",
SystemTime.getOffsetTime(msLater), new TimerEventPerformer() {
public void perform(TimerEvent event) {
if (!display.isDisposed()) {
display.asyncExec(runnableWrapper);
}
}
});
}
}
}
} catch (NullPointerException e) {
// If the display is being disposed of, asyncExec may give a null
// pointer error
return false;
}
} else {
display.syncExec(code);
}
return true;
}
/**
* Execute code in the Runnable object using SWT's thread. If current
* thread it already SWT's thread, the code will run immediately. If the
* current thread is not SWT's, code will be run asynchronously on SWT's
* thread at the next reasonable opportunity.
*
* This method does not catch any exceptions.
*
* @param code code to run
* @return success
*/
public static boolean execSWTThread(Runnable code) {
return execSWTThread(code, -1);
}
public static boolean isThisThreadSWT() {
SWTThread swt = SWTThread.getInstance();
if (swt == null) {
//System.err.println("WARNING: SWT Thread not started yet");
}
Display display = (swt == null) ? Display.getCurrent() : swt.getDisplay();
if (display == null) {
return false;
}
// This will throw if we are disposed or on the wrong thread
// Much better that display.getThread() as that one locks Device.class
// and may end up causing sync lock when disposing
try {
display.getWarnings();
} catch (SWTException e) {
return false;
}
return (display.getThread() == Thread.currentThread());
}
/**
* Bottom Index may be negative. Returns bottom index even if invisible.
*/
public static int getTableBottomIndex(TableOrTreeSWT table, int iTopIndex) {
// Shortcut: if lastBottomIndex is present, assume it's accurate
Object lastBottomIndex = table.getData("lastBottomIndex");
if (lastBottomIndex instanceof Number) {
return ((Number)lastBottomIndex).intValue();
}
int columnCount = table.getColumnCount();
if (columnCount == 0) {
return -1;
}
int xPos = table.getColumn(0).getWidth() - 1;
if (columnCount > 1) {
xPos += table.getColumn(1).getWidth();
}
Rectangle clientArea = table.getClientArea();
TableItemOrTreeItem bottomItem = table.getItem(new Point(xPos,
clientArea.y + clientArea.height - 2));
if (bottomItem != null) {
while (bottomItem.getParentItem() != null) {
bottomItem = bottomItem.getParentItem();
}
return table.indexOf(bottomItem);
}
return table.getItemCount() - 1;
}
public static List<TableItemOrTreeItem> getVisibleTableItems(TableOrTreeSWT table) {
if (table.getColumnCount() < 2) {
return Collections.emptyList();
}
int xPos = table.getColumn(0).getWidth() + table.getColumn(1).getWidth() - 1;
Rectangle clientArea = table.getClientArea();
TableItemOrTreeItem bottomItem = table.getItem(new Point(xPos,
clientArea.y + clientArea.height - 1));
if (bottomItem == null) {
if (clientArea.height + clientArea.y <= 0) {
return Collections.emptyList();
}
}
TableItemOrTreeItem curItem = table.getTopItem();
if (curItem == null) {
if (table.getItemCount() > 0) {
// BUG in GTK: topitem of tree will be null after setItemCount
curItem = table.getItem(0);
} else {
return Collections.emptyList();
}
}
List<TableItemOrTreeItem> items = new ArrayList<TableItemOrTreeItem>();
int i = table.indexOf(curItem);
int count = table.getItemCount();
while (true) {
if (curItem == bottomItem) {
items.add(curItem);
break;
} else if (curItem == null) {
break;
}
items.add(curItem);
if (curItem.getExpanded() && curItem.getItemCount() > 0) {
if (!addItemsToList(items, curItem.getItems(), bottomItem)) {
break;
}
}
i++;
if (i >= count) {
break;
}
curItem = table.getItem(i);
}
return items;
}
private static boolean addItemsToList(List<TableItemOrTreeItem> list, TableItemOrTreeItem[] items,
TableItemOrTreeItem stopOnItem) {
for (TableItemOrTreeItem item : items) {
list.add(item);
if (item == stopOnItem) {
return false;
}
}
return true;
}
public static void launch( final DiskManagerFileInfo fileInfo ){
LaunchManager launch_manager = LaunchManager.getManager();
LaunchManager.LaunchTarget target = launch_manager.createTarget( fileInfo );
launch_manager.launchRequest(
target,
new LaunchManager.LaunchAction()
{
public void
actionAllowed()
{
Utils.execSWTThread(
new Runnable()
{
public void
run()
{
launch(fileInfo.getFile(true).toString());
}
});
}
public void
actionDenied(
Throwable reason )
{
Debug.out( "Launch request denied", reason );
}
});
}
public static void launch(String sFile) {
if (sFile == null || sFile.trim().length() == 0) {
return;
}
if (!Constants.isWindows && new File(sFile).isDirectory()) {
PlatformManager mgr = PlatformManagerFactory.getPlatformManager();
if (mgr.hasCapability(PlatformManagerCapabilities.ShowFileInBrowser)) {
try {
PlatformManagerFactory.getPlatformManager().showFile(sFile);
return;
} catch (PlatformManagerException e) {
}
}
}
sFile = sFile.replaceAll( "&vzemb=1", "" );
boolean launched = Program.launch(sFile);
if (!launched && Constants.isUnix) {
sFile = sFile.replaceAll( " ", "\\ " );
if (!Program.launch("xdg-open " + sFile)) {
if ( !Program.launch("htmlview " + sFile)){
Debug.out( "Failed to launch '" + sFile + "'" );
}
}
}
}
/**
* Sets the checkbox in a Virtual Table while inside a SWT.SetData listener
* trigger. SWT 3.1 has an OSX bug that needs working around.
*
* @param item
* @param checked
*/
public static void setCheckedInSetData(final TableItem item,
final boolean checked) {
item.setChecked(checked);
if (Constants.isWindowsXP || isGTK) {
Rectangle r = item.getBounds(0);
Table table = item.getParent();
Rectangle rTable = table.getClientArea();
table.redraw(0, r.y, rTable.width, r.height, true);
}
}
public static boolean linkShellMetricsToConfig(final Shell shell,
final String sConfigPrefix) {
boolean isMaximized = COConfigurationManager.getBooleanParameter(sConfigPrefix
+ ".maximized");
if (!isMaximized) {
shell.setMaximized(false);
}
String windowRectangle = COConfigurationManager.getStringParameter(
sConfigPrefix + ".rectangle", null);
boolean bDidResize = false;
if (null != windowRectangle) {
int i = 0;
int[] values = new int[4];
StringTokenizer st = new StringTokenizer(windowRectangle, ",");
try {
while (st.hasMoreTokens() && i < 4) {
values[i++] = Integer.valueOf(st.nextToken()).intValue();
}
if (i == 4) {
Rectangle shellBounds = new Rectangle(values[0], values[1],
values[2], values[3]);
if (shellBounds.width > 100 && shellBounds.height > 50) {
shell.setBounds(shellBounds);
verifyShellRect(shell, true);
bDidResize = true;
}
}
} catch (Exception e) {
}
}
if (isMaximized) {
shell.setMaximized(isMaximized);
}
new ShellMetricsResizeListener(shell, sConfigPrefix);
return bDidResize;
}
private static class ShellMetricsResizeListener
implements Listener
{
private int state = -1;
private String sConfigPrefix;
private Rectangle bounds = null;
ShellMetricsResizeListener(Shell shell, String sConfigPrefix) {
this.sConfigPrefix = sConfigPrefix;
state = calcState(shell);
if (state == SWT.NONE)
bounds = shell.getBounds();
shell.addListener(SWT.Resize, this);
shell.addListener(SWT.Move, this);
shell.addListener(SWT.Dispose, this);
}
private int calcState(Shell shell) {
return shell.getMinimized() ? SWT.MIN : shell.getMaximized()
&& !isCarbon ? SWT.MAX : SWT.NONE;
}
private void saveMetrics() {
COConfigurationManager.setParameter(sConfigPrefix + ".maximized",
state == SWT.MAX);
if (bounds == null)
return;
COConfigurationManager.setParameter(sConfigPrefix + ".rectangle",
bounds.x + "," + bounds.y + "," + bounds.width + "," + bounds.height);
COConfigurationManager.save();
}
public void handleEvent(Event event) {
Shell shell = (Shell) event.widget;
state = calcState(shell);
if (event.type != SWT.Dispose && state == SWT.NONE)
bounds = shell.getBounds();
if (event.type == SWT.Dispose)
saveMetrics();
}
}
public static GridData setGridData(Composite composite, int gridStyle,
Control ctrlBestSize, int maxHeight) {
GridData gridData = new GridData(gridStyle);
gridData.heightHint = ctrlBestSize.computeSize(SWT.DEFAULT, SWT.DEFAULT).y;
if (gridData.heightHint > maxHeight && maxHeight > 0)
gridData.heightHint = maxHeight;
composite.setLayoutData(gridData);
return gridData;
}
public static FormData getFilledFormData() {
FormData formData = new FormData();
formData.top = new FormAttachment(0, 0);
formData.left = new FormAttachment(0, 0);
formData.right = new FormAttachment(100, 0);
formData.bottom = new FormAttachment(100, 0);
return formData;
}
public static int pixelsToPoint(int pixels, int dpi) {
int ret = (int) Math.round((pixels * 72.0) / dpi);
return ret;
}
private static int pixelsToPoint(double pixels, int dpi) {
int ret = (int) Math.round((pixels * 72.0) / dpi);
return ret;
}
private static boolean drawImage(GC gc, Image image, Rectangle dstRect,
Rectangle clipping, int hOffset, int vOffset, boolean clearArea) {
return drawImage(gc, image, new Point(0, 0), dstRect, clipping, hOffset,
vOffset, clearArea);
}
private static boolean drawImage(GC gc, Image image, Rectangle dstRect,
Rectangle clipping, int hOffset, int vOffset) {
return drawImage(gc, image, new Point(0, 0), dstRect, clipping, hOffset,
vOffset, false);
}
public static boolean drawImage(GC gc, Image image, Point srcStart,
Rectangle dstRect, Rectangle clipping, int hOffset, int vOffset,
boolean clearArea) {
Rectangle srcRect;
Point dstAdj;
if (clipping == null) {
dstAdj = new Point(0, 0);
srcRect = new Rectangle(srcStart.x, srcStart.y, dstRect.width,
dstRect.height);
} else {
if (!dstRect.intersects(clipping)) {
return false;
}
dstAdj = new Point(Math.max(0, clipping.x - dstRect.x), Math.max(0,
clipping.y - dstRect.y));
srcRect = new Rectangle(0, 0, 0, 0);
srcRect.x = srcStart.x + dstAdj.x;
srcRect.y = srcStart.y + dstAdj.y;
srcRect.width = Math.min(dstRect.width - dstAdj.x, clipping.x
+ clipping.width - dstRect.x);
srcRect.height = Math.min(dstRect.height - dstAdj.y, clipping.y
+ clipping.height - dstRect.y);
}
if (!srcRect.isEmpty()) {
try {
if (clearArea) {
gc.fillRectangle(dstRect.x + dstAdj.x + hOffset, dstRect.y + dstAdj.y
+ vOffset, srcRect.width, srcRect.height);
}
gc.drawImage(image, srcRect.x, srcRect.y, srcRect.width,
srcRect.height, dstRect.x + dstAdj.x + hOffset, dstRect.y
+ dstAdj.y + vOffset, srcRect.width, srcRect.height);
} catch (Exception e) {
System.out.println("drawImage: " + e.getMessage() + ": " + image + ", "
+ srcRect + ", " + (dstRect.x + dstAdj.y + hOffset) + ","
+ (dstRect.y + dstAdj.y + vOffset) + "," + srcRect.width + ","
+ srcRect.height + "; imageBounds = " + image.getBounds());
}
}
return true;
}
public static Control
findChild(
Composite comp,
int x,
int y )
{
Rectangle comp_bounds = comp.getBounds();
if ( comp.isVisible() && comp_bounds.contains( x, y )){
x -= comp_bounds.x;
y -= comp_bounds.y;
Control[] children = comp.getChildren();
for ( int i = 0; i < children.length; i++){
Control child = children[i];
if ( child.isVisible()){
if (child instanceof Composite) {
Control res = findChild((Composite) child, x, y );
if ( res != null ){
return( res );
}
}else{
return( child );
}
}
}
return( comp );
}
return( null );
}
/**
* @param area
* @param event id
* @param listener
*/
public static void addListenerAndChildren(Composite area, int event,
Listener listener) {
area.addListener(event, listener);
Control[] children = area.getChildren();
for (int i = 0; i < children.length; i++) {
Control child = children[i];
if (child instanceof Composite) {
addListenerAndChildren((Composite) child, event, listener);
} else {
child.addListener(event, listener);
}
}
}
public static Shell findAnyShell() {
// Pick the main shell if we can
UIFunctionsSWT uiFunctions = UIFunctionsManagerSWT.getUIFunctionsSWT();
if (uiFunctions != null) {
Shell shell = uiFunctions.getMainShell();
if (shell != null && !shell.isDisposed()) {
return shell;
}
}
// Get active shell from current display if we can
Display current = Display.getCurrent();
if (current == null) {
return null;
}
Shell shell = current.getActiveShell();
if (shell != null && !shell.isDisposed()) {
return shell;
}
// Get first shell of current display if we can
Shell[] shells = current.getShells();
if (shells.length == 0) {
return null;
}
if (shells[0] != null && !shells[0].isDisposed()) {
return shells[0];
}
return null;
}
/**
* @param listener
*/
public static boolean verifyShellRect(Shell shell, boolean bAdjustIfInvalid) {
boolean bMetricsOk;
try {
bMetricsOk = false;
Point ptTopLeft = shell.getLocation();
Monitor[] monitors = shell.getDisplay().getMonitors();
for (int j = 0; j < monitors.length && !bMetricsOk; j++) {
Rectangle bounds = monitors[j].getBounds();
bMetricsOk = bounds.contains(ptTopLeft);
}
} catch (NoSuchMethodError e) {
Rectangle bounds = shell.getDisplay().getBounds();
bMetricsOk = shell.getBounds().intersects(bounds);
}
if (!bMetricsOk && bAdjustIfInvalid) {
centreWindow(shell);
}
return bMetricsOk;
}
/**
* Relayout all composites up from control until there's enough room for the
* control to fit
*
* @param control Control that had it's sized changed and needs more room
*/
public static void relayout(Control control) {
relayout(control, false);
}
/**
* Relayout all composites up from control until there's enough room for the
* control to fit
*
* @param control Control that had it's sized changed and needs more room
*/
public static void relayout(Control control, boolean expandOnly) {
if (control == null || control.isDisposed() || !control.isVisible()) {
return;
}
Composite parent = control.getParent();
Point targetSize = control.computeSize(SWT.DEFAULT, SWT.DEFAULT, true);
Point size = control.getSize();
if (size.y == targetSize.y && size.x == targetSize.x) {
return;
}
int fixedWidth = -1;
int fixedHeight = -1;
Object layoutData = control.getLayoutData();
if (layoutData instanceof FormData) {
FormData fd = (FormData) layoutData;
fixedHeight = fd.height;
fixedWidth = fd.width;
if (fd.width != SWT.DEFAULT && fd.height != SWT.DEFAULT) {
parent.layout();
return;
}
}
if (expandOnly && size.y >= targetSize.y && size.x >= targetSize.x) {
parent.layout();
return;
}
while (parent != null) {
parent.layout(true, true);
parent = parent.getParent();
Point newSize = control.getSize();
//System.out.println("new=" + newSize + ";target=" + targetSize);
if ((fixedHeight > -1 || (newSize.y >= targetSize.y))
&& (fixedWidth > -1 || (newSize.x >= targetSize.x))) {
break;
}
}
if (parent != null) {
parent.layout();
}
}
/**
*
*/
public static void beep() {
execSWTThread(new AERunnable() {
public void runSupport() {
Display display = Display.getDefault();
if (display != null) {
display.beep();
}
}
});
}
/**
* @deprecated Use {@link #execSWTThread(AERunnableWithCallback)} to avoid
* thread locking issues
*/
public static boolean execSWTThreadWithBool(String ID, AERunnableBoolean code) {
return execSWTThreadWithBool(ID, code, 0);
}
/**
* Runs code within the SWT thread, waits for code to complete executing,
* (using a semaphore), and then returns a value.
*
* @note USE WITH CAUTION. If the calling function synchronizes, and the
* runnable code ends up synchronizing on the same object, an indefinite
* thread lock or an unexpected timeout may occur (if one of the threads
* is the SWT thread).<p>
* ex - Thread1 calls c.foo(), which synchronized(this).
* - Thread2 is the SWT Thread. Thread2 calls c.foo(), which waits on
* Thread1 to complete.
* - c.foo() from Thread1 calls execSWTThreadWithBoolean(.., swtcode, ..),
* which waits for the SWT Thread to return run the swtcode.
* - Deadlock, or Timoeout which returns a false (and no code ran)
*
* @param ID id for debug
* @param code code to run
* @param millis ms to timeout in
* @return
*/
public static boolean execSWTThreadWithBool(String ID,
AERunnableBoolean code, long millis) {
if (code == null) {
return false;
}
boolean[] returnValueObject = {
false
};
Display display = getDisplay();
AESemaphore sem = null;
if (display == null || display.getThread() != Thread.currentThread()) {
sem = new AESemaphore(ID);
}
try {
code.setupReturn(ID, returnValueObject, sem);
if (!execSWTThread(code)) {
// code never got run
// XXX: throw instead?
return false;
}
} catch (Throwable e) {
if (sem != null) {
sem.release();
}
Debug.out(ID, e);
}
if (sem != null) {
sem.reserve(millis);
}
return returnValueObject[0];
}
/**
* @deprecated Use {@link #execSWTThread(AERunnableWithCallback)} to avoid
* thread locking issues
*/
public static Object execSWTThreadWithObject(String ID, AERunnableObject code) {
return execSWTThreadWithObject(ID, code, 0);
}
/**
* Runs code within the SWT thread, waits for code to complete executing,
* (using a semaphore), and then returns a value.
*
* @note USE WITH CAUTION. If the calling function synchronizes, and the
* runnable code ends up synchronizing on the same object, an indefinite
* thread lock or an unexpected timeout may occur (if one of the threads
* is the SWT thread).<p>
* ex - Thread1 calls c.foo(), which synchronized(this).
* - Thread2 is the SWT Thread. Thread2 calls c.foo(), which waits on
* Thread1 to complete.
* - c.foo() from Thread1 calls execSWTThreadWithObject(.., swtcode, ..),
* which waits for the SWT Thread to return run the swtcode.
* - Deadlock, or Timoeout which returns a null (and no code ran)
*
* @param ID id for debug
* @param code code to run
* @param millis ms to timeout in
* @return
*/
public static Object execSWTThreadWithObject(String ID,
AERunnableObject code, long millis) {
if (code == null) {
return null;
}
Object[] returnValueObject = {
null
};
Display display = getDisplay();
AESemaphore sem = null;
if (display == null || display.getThread() != Thread.currentThread()) {
sem = new AESemaphore(ID);
}
try {
code.setupReturn(ID, returnValueObject, sem);
if (!execSWTThread(code)) {
// XXX: throw instead?
return null;
}
} catch (Throwable e) {
if (sem != null) {
sem.releaseForever();
}
Debug.out(ID, e);
}
if (sem != null) {
sem.reserve(millis);
}
return returnValueObject[0];
}
/**
* Waits until modal dialogs are disposed. Assumes we are on SWT thread
*
* @since 3.0.1.3
*/
public static void waitForModals() {
SWTThread swt = SWTThread.getInstance();
Display display;
if (swt == null) {
display = Display.getDefault();
if (display == null) {
System.err.println("SWT Thread not started yet!");
return;
}
} else {
if (swt.isTerminated()) {
return;
}
display = swt.getDisplay();
}
if (display == null || display.isDisposed()) {
return;
}
Shell[] shells = display.getShells();
Shell modalShell = null;
for (int i = 0; i < shells.length; i++) {
Shell shell = shells[i];
if ((shell.getStyle() & SWT.APPLICATION_MODAL) > 0) {
modalShell = shell;
break;
}
}
if (modalShell != null) {
while (!modalShell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
}
public static GridData getWrappableLabelGridData(int hspan, int styles) {
GridData gridData = new GridData(GridData.HORIZONTAL_ALIGN_FILL | styles);
gridData.horizontalSpan = hspan;
gridData.widthHint = 0;
return gridData;
}
private static Image createAlphaImage(Device device, int width, int height) {
return createAlphaImage(device, width, height, (byte) 0);
}
public static Image createAlphaImage(Device device, int width, int height,
byte defaultAlpha) {
byte[] alphaData = new byte[width * height];
Arrays.fill(alphaData, 0, alphaData.length, (byte) defaultAlpha);
ImageData imageData = new ImageData(width, height, 24, new PaletteData(
0xFF, 0xFF00, 0xFF0000));
Arrays.fill(imageData.data, 0, imageData.data.length, (byte) 0);
imageData.alphaData = alphaData;
if (device == null) {
device = Display.getDefault();
}
Image image = new Image(device, imageData);
return image;
}
public static Image blitImage(Device device, Image srcImage,
Rectangle srcArea, Image dstImage, Point dstPos) {
if (srcArea == null) {
srcArea = srcImage.getBounds();
}
Rectangle dstBounds = dstImage.getBounds();
if (dstPos == null) {
dstPos = new Point(dstBounds.x, dstBounds.y);
} else {
dstBounds.x = dstPos.x;
dstBounds.y = dstPos.y;
}
ImageData dstImageData = dstImage.getImageData();
ImageData srcImageData = srcImage.getImageData();
int yPos = dstPos.y;
int[] pixels = new int[srcArea.width];
byte[] alphas = new byte[srcArea.width];
for (int y = 0; y < srcArea.height; y++) {
srcImageData.getPixels(srcArea.x, y + srcArea.y, srcArea.width, pixels, 0);
dstImageData.setPixels(dstPos.x, yPos, srcArea.width, pixels, 0);
srcImageData.getAlphas(srcArea.x, y + srcArea.y, srcArea.width, alphas, 0);
dstImageData.setAlphas(dstPos.x, yPos, srcArea.width, alphas, 0);
yPos++;
}
return new Image(device, dstImageData);
}
/**
* Draws diagonal stripes onto the specified area of a GC
* @param lineDist spacing between the individual lines
* @param leftshift moves the stripes to the left, useful to shift with the background
* @param fallingLines true for top left to bottom-right lines, false otherwise
*/
public static void drawStriped(GC gcImg, int x, int y, int width, int height,
int lineDist, int leftshift, boolean fallingLines) {
lineDist += 2;
final int xm = x + width;
final int ym = y + height;
for (int i = x; i < xm; i++) {
for (int j = y; j < ym; j++) {
if ((i + leftshift + (fallingLines ? -j : j)) % lineDist == 0)
gcImg.drawPoint(i, j);
}
}
}
/**
*
* @param display
* @param background
* @param foreground
* @param foregroundOffsetOnBg
* @param modifyForegroundAlpha 0 (fully transparent) to 255 (retain current alpha)
* @return
*/
public static Image renderTransparency(Display display, Image background,
Image foreground, Point foregroundOffsetOnBg, int modifyForegroundAlpha) {
//Checks
if (display == null || display.isDisposed() || background == null
|| background.isDisposed() || foreground == null
|| foreground.isDisposed())
return null;
Rectangle backgroundArea = background.getBounds();
Rectangle foregroundDrawArea = foreground.getBounds();
foregroundDrawArea.x += foregroundOffsetOnBg.x;
foregroundDrawArea.y += foregroundOffsetOnBg.y;
foregroundDrawArea.intersect(backgroundArea);
if (foregroundDrawArea.isEmpty())
return null;
Image image = new Image(display, backgroundArea);
ImageData backData = background.getImageData();
ImageData foreData = foreground.getImageData();
ImageData imgData = image.getImageData();
PaletteData backPalette = backData.palette;
ImageData backMask = backData.getTransparencyType() != SWT.TRANSPARENCY_ALPHA
? backData.getTransparencyMask() : null;
PaletteData forePalette = foreData.palette;
ImageData foreMask = foreData.getTransparencyType() != SWT.TRANSPARENCY_ALPHA
? foreData.getTransparencyMask() : null;
PaletteData imgPalette = imgData.palette;
image.dispose();
for (int x = 0; x < backgroundArea.width; x++) {
for (int y = 0; y < backgroundArea.height; y++) {
RGB cBack = backPalette.getRGB(backData.getPixel(x, y));
int aBack = backData.getAlpha(x, y);
if (backMask != null && backMask.getPixel(x, y) == 0)
aBack = 0; // special treatment for icons with transparency masks
int aFore = 0;
if (foregroundDrawArea.contains(x, y)) {
final int fx = x - foregroundDrawArea.x;
final int fy = y - foregroundDrawArea.y;
RGB cFore = forePalette.getRGB(foreData.getPixel(fx, fy));
aFore = foreData.getAlpha(fx, fy);
if (foreMask != null && foreMask.getPixel(fx, fy) == 0)
aFore = 0; // special treatment for icons with transparency masks
aFore = aFore * modifyForegroundAlpha / 255;
cBack.red *= aBack * (255 - aFore);
cBack.red /= 255;
cBack.red += aFore * cFore.red;
cBack.red /= 255;
cBack.green *= aBack * (255 - aFore);
cBack.green /= 255;
cBack.green += aFore * cFore.green;
cBack.green /= 255;
cBack.blue *= aBack * (255 - aFore);
cBack.blue /= 255;
cBack.blue += aFore * cFore.blue;
cBack.blue /= 255;
}
imgData.setAlpha(x, y, aFore + aBack * (255 - aFore) / 255);
imgData.setPixel(x, y, imgPalette.getPixel(cBack));
}
}
return new Image(display, imgData);
}
public static Control findBackgroundImageControl(Control control) {
Image image = control.getBackgroundImage();
if (image == null) {
return control;
}
Composite parent = control.getParent();
Composite lastParent = parent;
while (parent != null) {
Image parentImage = parent.getBackgroundImage();
if (!image.equals(parentImage)) {
return lastParent;
}
lastParent = parent;
parent = parent.getParent();
}
return control;
}
/**
* @return
*
* @since 3.0.3.5
*/
public static boolean anyShellHaveStyle(int styles) {
Display display = Display.getCurrent();
if (display != null) {
Shell[] shells = display.getShells();
for (int i = 0; i < shells.length; i++) {
Shell shell = shells[i];
int style = shell.getStyle();
if ((style & styles) == styles) {
return true;
}
}
}
return false;
}
public static Shell findFirstShellWithStyle(int styles) {
Display display = Display.getCurrent();
if (display != null) {
Shell[] shells = display.getShells();
for (int i = 0; i < shells.length; i++) {
Shell shell = shells[i];
int style = shell.getStyle();
if ((style & styles) == styles && !shell.isDisposed()) {
return shell;
}
}
}
return null;
}
public static int[] colorToIntArray(Color color) {
if (color == null || color.isDisposed()) {
return null;
}
return new int[] {
color.getRed(),
color.getGreen(),
color.getBlue()
};
}
/**
* Centers the target <code>Rectangle</code> relative to the reference Rectangle
* @param target
* @param reference
*/
public static void centerRelativeTo(Rectangle target, Rectangle reference) {
target.x = reference.x + (reference.width / 2) - target.width / 2;
target.y = reference.y + (reference.height / 2) - target.height / 2;
}
/**
* Ensure that the given <code>Rectangle</code> is fully visible on the monitor that the cursor
* is currently in. This method does not resize the given Rectangle; it merely reposition it
* if appropriate. If the given Rectangle is taller or wider than the current monitor then
* it may not fit 'fully' in the monitor.
* <P>
* We use a best-effort approach with an emphasis to have at least the top-left of the Rectangle
* be visible. If the given Rectangle does not fit entirely in the monitor then portion
* of the right and/or left may be off-screen.
*
* <P>
* This method does honor global screen elements when possible. Screen elements include the TaskBar on Windows
* and the Application menu on OSX, and possibly others. The re-positioned Rectangle returned will fit on the
* screen without overlapping (or sliding under) these screen elements.
* @param rect
* @return
*/
public static void makeVisibleOnCursor(Rectangle rect) {
if (null == rect) {
return;
}
Display display = Display.getCurrent();
if (null == display) {
Debug.out("No current display detected. This method [Utils.makeVisibleOnCursor()] must be called from a display thread.");
return;
}
try {
/*
* Get cursor location
*/
Point cursorLocation = display.getCursorLocation();
/*
* Make visible on the monitor that the mouse cursor resides in
*/
makeVisibleOnMonitor(rect, getMonitor(cursorLocation));
} catch (Throwable t) {
//Do nothing
}
}
/**
* Ensure that the given <code>Rectangle</code> is fully visible on the given <code>Monitor</code>.
* This method does not resize the given Rectangle; it merely reposition it if appropriate.
* If the given Rectangle is taller or wider than the current monitor then it may not fit 'fully' in the monitor.
* <P>
* We use a best-effort approach with an emphasis to have at least the top-left of the Rectangle
* be visible. If the given Rectangle does not fit entirely in the monitor then portion
* of the right and/or left may be off-screen.
*
* <P>
* This method does honor global screen elements when possible. Screen elements include the TaskBar on Windows
* and the Application menu on OSX, and possibly others. The re-positioned Rectangle returned will fit on the
* screen without overlapping (or sliding under) these screen elements.
* @param rect
* @param monitor
*/
public static void makeVisibleOnMonitor(Rectangle rect, Monitor monitor) {
if (null == rect || null == monitor) {
return;
}
try {
Rectangle monitorBounds = monitor.getClientArea();
/*
* Make sure the bottom is fully visible on the monitor
*/
int bottomDiff = (monitorBounds.y + monitorBounds.height)
- (rect.y + rect.height);
if (bottomDiff < 0) {
rect.y += bottomDiff;
}
/*
* Make sure the right is fully visible on the monitor
*/
int rightDiff = (monitorBounds.x + monitorBounds.width)
- (rect.x + rect.width);
if (rightDiff < 0) {
rect.x += rightDiff;
}
/*
* Make sure the left is fully visible on the monitor
*/
if (rect.x < monitorBounds.x) {
rect.x = monitorBounds.x;
}
/*
* Make sure the top is fully visible on the monitor
*/
if (rect.y < monitorBounds.y) {
rect.y = monitorBounds.y;
}
} catch (Throwable t) {
//Do nothing
}
}
/**
* Returns the <code>Monitor</code> that the given x,y coordinates resides in
* @param x
* @param y
* @return the monitor if found; otherwise returns <code>null</code>
*/
private static Monitor getMonitor(int x, int y) {
return getMonitor(new Point(x, y));
}
/**
* Returns the <code>Monitor</code> that the given <code>Point</code> resides in
* @param location
* @return the monitor if found; otherwise returns <code>null</code>
*/
public static Monitor getMonitor(Point location) {
Display display = Display.getCurrent();
if (null == display) {
Debug.out("No current display detected. This method [Utils.makeVisibleOnCursor()] must be called from a display thread.");
return null;
}
try {
/*
* Find the monitor that this location resides in
*/
Monitor[] monitors = display.getMonitors();
Rectangle monitorBounds = null;
for (int i = 0; i < monitors.length; i++) {
monitorBounds = monitors[i].getClientArea();
if (true == monitorBounds.contains(location)) {
return monitors[i];
}
}
} catch (Throwable t) {
//Do nothing
}
return null;
}
private static boolean gotBrowserStyle = false;
private static int browserStyle = SWT.NONE;
/**
* Consistently applies the browser style obtained during the first invocation
* @param style the style you wish to apply
* @return the style, possibly ORed with <code>SWT.MOZILLA</code>
*/
public static int getInitialBrowserStyle(int style) {
if (!gotBrowserStyle) {
browserStyle = COConfigurationManager.getBooleanParameter("swt.forceMozilla")
? SWT.MOZILLA : SWT.NONE;
gotBrowserStyle = true;
}
return style | browserStyle;
}
public static final long IMMEDIATE_ADDREMOVE_DELAY = 150;
private static final long IMMEDIATE_ADDREMOVE_MAXDELAY = 2000;
private static Timer timerProcessDataSources = new Timer("Process Data Sources");
private static TimerEvent timerEventProcessDS;
private static List processDataSourcesOutstanding = new ArrayList();
public static boolean
addDataSourceAggregated(
addDataSourceCallback callback )
{
if ( callback == null ){
return( true );
}
boolean processQueueImmediately = false;
List to_do_now = null;
synchronized( timerProcessDataSources ){
if ( timerEventProcessDS != null && !timerEventProcessDS.hasRun()){
// Push timer forward, unless we've pushed it forward for over x seconds
long now = SystemTime.getCurrentTime();
if (now - timerEventProcessDS.getCreatedTime() < IMMEDIATE_ADDREMOVE_MAXDELAY) {
long lNextTime = now + IMMEDIATE_ADDREMOVE_DELAY;
timerProcessDataSources.adjustAllBy( lNextTime - timerEventProcessDS.getWhen());
if ( !processDataSourcesOutstanding.contains( callback )){
processDataSourcesOutstanding.add( callback );
}
}else{
timerEventProcessDS.cancel();
timerEventProcessDS = null;
processQueueImmediately = true;
to_do_now = processDataSourcesOutstanding;
processDataSourcesOutstanding = new ArrayList();
}
}else{
if ( !processDataSourcesOutstanding.contains( callback )){
processDataSourcesOutstanding.add( callback );
}
timerEventProcessDS =
timerProcessDataSources.addEvent(
SystemTime.getCurrentTime() + IMMEDIATE_ADDREMOVE_DELAY,
new TimerEventPerformer()
{
public void
perform(
TimerEvent event )
{
List to_do;
synchronized( timerProcessDataSources ){
timerEventProcessDS = null;
to_do = processDataSourcesOutstanding;
processDataSourcesOutstanding = new ArrayList();
}
for (int i=0;i<to_do.size();i++){
try{
addDataSourceCallback this_callback = (addDataSourceCallback)to_do.get(i);
if (TableViewSWT.DEBUGADDREMOVE ) {
this_callback.debug("processDataSourceQueue after "
+ (SystemTime.getCurrentTime() - event.getCreatedTime())
+ "ms");
}
this_callback.process();
}catch( Throwable e ){
Debug.printStackTrace(e);
}
}
}
});
}
if ( to_do_now != null ){
// process outside the synchronized block, otherwise we'll end up with deadlocks
to_do_now.remove( callback );
for (int i=0;i<to_do_now.size();i++){
try{
addDataSourceCallback this_callback = (addDataSourceCallback)to_do_now.get(i);
if ( TableViewSWT.DEBUGADDREMOVE ){
this_callback.debug("Over immediate delay limit, processing queue now");
}
this_callback.process();
}catch( Throwable e ){
Debug.printStackTrace(e);
}
}
}
}
return( processQueueImmediately );
}
public interface
addDataSourceCallback
{
public void
process();
public void
debug(
String str );
}
private static Map truncatedTextCache = new HashMap();
private static ThreadPool tp = new ThreadPool("GetOffSWT", 3);
private static class TruncatedTextResult {
String text;
int maxWidth;
public TruncatedTextResult() {
}
}
public synchronized static String truncateText(GC gc,String text, int maxWidth,boolean cache) {
if(cache) {
TruncatedTextResult result = (TruncatedTextResult) truncatedTextCache.get(text);
if(result != null && result.maxWidth == maxWidth) {
return result.text;
}
}
StringBuffer sb = new StringBuffer(text);
String append = "...";
int appendWidth = gc.textExtent(append).x;
boolean needsAppend = false;
while(gc.textExtent(sb.toString()).x > maxWidth) {
//Remove characters until they fit into the maximum width
sb.deleteCharAt(sb.length()-1);
needsAppend = true;
if(sb.length() == 1) {
break;
}
}
if(needsAppend) {
while(gc.textExtent(sb.toString()).x + appendWidth > maxWidth) {
//Remove characters until they fit into the maximum width
sb.deleteCharAt(sb.length()-1);
needsAppend = true;
if(sb.length() == 1) {
break;
}
}
sb.append(append);
}
if(cache) {
TruncatedTextResult ttR = new TruncatedTextResult();
ttR.text = sb.toString();
ttR.maxWidth = maxWidth;
truncatedTextCache.put(text, ttR);
}
return sb.toString();
}
/**
* @param bg
* @return
*
* @since 3.1.1.1
*/
public static String toColorHexString(Color bg) {
StringBuffer sb = new StringBuffer();
twoHex(sb, bg.getRed());
twoHex(sb, bg.getGreen());
twoHex(sb, bg.getBlue());
return sb.toString();
}
private static void twoHex(StringBuffer sb, int h) {
if (h <= 15) {
sb.append('0');
}
sb.append(Integer.toHexString(h));
}
public static String
getWidgetBGColorURLParam()
{
Color bg = findAnyShell().getDisplay().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND);
byte[] color = new byte[3];
color[0] = (byte) bg.getRed();
color[1] = (byte) bg.getGreen();
color[2] = (byte) bg.getBlue();
return( "bg_color=" + ByteFormatter.nicePrint(color));
}
public static void
reportError(
Throwable e )
{
MessageBoxShell mb =
new MessageBoxShell(
MessageText.getString("ConfigView.section.security.op.error.title"),
MessageText.getString("ConfigView.section.security.op.error",
new String[] {
Debug.getNestedExceptionMessage(e)
}),
new String[] {
MessageText.getString("Button.ok"),
},
0 );
mb.open(null);
}
public static void getOffOfSWTThread(AERunnable runnable) {
tp.run(runnable);
}
public static Browser createSafeBrowser(Composite parent, int style) {
try {
Browser browser = new Browser(parent, Utils.getInitialBrowserStyle(style));
browser.addDisposeListener(new DisposeListener() {
public void widgetDisposed(DisposeEvent e) {
((Browser)e.widget).setUrl("about:blank");
((Browser)e.widget).setVisible(false);
while (!e.display.isDisposed() && e.display.readAndDispatch());
}
});
return browser;
} catch (Throwable e) {
}
return null;
}
public static int getUserMode() {
return userMode;
}
public static Point getLocationRelativeToShell(Control table) {
Point controlLocation = table.toDisplay(table.getLocation());
Point shellLocation = table.getShell().getLocation();
return new Point(controlLocation.x - shellLocation.x, controlLocation.y - shellLocation.y);
}
}