/*
* 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.util.*;
import java.util.List;
import org.eclipse.swt.SWT;
import org.eclipse.swt.SWTException;
import org.eclipse.swt.custom.ScrolledComposite;
import org.eclipse.swt.dnd.*;
import org.eclipse.swt.events.ControlAdapter;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.layout.*;
import org.eclipse.swt.program.Program;
import org.eclipse.swt.widgets.*;
import org.gudy.azureus2.core3.config.COConfigurationManager;
import org.gudy.azureus2.core3.internat.MessageText;
import org.gudy.azureus2.core3.util.*;
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.utils.VerticalAligner;
import com.aelitis.azureus.core.impl.AzureusCoreImpl;
import com.aelitis.azureus.ui.swt.UIFunctionsManagerSWT;
import com.aelitis.azureus.ui.swt.UIFunctionsSWT;
/**
* @author Olivier
*
*/
public class Utils
{
private static final String GOOD_STRING = "(/|,jI~`gy";
public static final boolean isGTK = SWT.getPlatform().equals("gtk");
/** 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;
private static final boolean DIRECT_SETCHECKED = !Constants.isOSX
|| SWT.getVersion() >= 3212;
public static final boolean SWT32_TABLEPAINT = false; //SWT.getVersion() >= 3200;
/**
* 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 queue;
private static AEDiagnosticsLogger diag_logger;
static {
if (DEBUG_SWTEXEC) {
queue = new ArrayList();
diag_logger = AEDiagnostics.getLogger("swt");
diag_logger.log("\n\nSWT Logging Starts");
} else {
queue = null;
diag_logger = null;
}
}
public static boolean isAZ2UI() {
String ui_type = COConfigurationManager.getStringParameter("ui");
return (ui_type.equals("az2"));
}
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) {
boolean bResourceObjectExists = SWT.getVersion() >= 3129;
for (int i = 0; i < disposeList.length; i++) {
Object o = disposeList[i];
if (o instanceof Widget && !((Widget) o).isDisposed())
((Widget) o).dispose();
else if (bResourceObjectExists && (o instanceof Resource)
&& !((Resource) o).isDisposed())
((Resource) o).dispose();
else {
try {
// For Pre-SWT 3.1
if ((o instanceof Cursor) && !((Cursor) o).isDisposed()) {
((Cursor) o).dispose();
} else if ((o instanceof Font) && !((Font) o).isDisposed()) {
((Font) o).dispose();
} else if ((o instanceof GC) && !((GC) o).isDisposed()) {
((GC) o).dispose();
} else if ((o instanceof Image) && !((Image) o).isDisposed()) {
((Image) o).dispose();
} else if ((o instanceof Region) && !((Region) o).isDisposed()) {
((Region) o).dispose(); // 3.0
} else if ((o instanceof TextLayout)
&& !((TextLayout) o).isDisposed()) {
((TextLayout) o).dispose(); // 3.0
}
} catch (NoClassDefFoundError e) {
// ignore
}
// Path, Pattern, Transform are all 3.1, which will be instances of
// Resource
}
}
}
/**
* 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
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;
if (SWT.getVersion() >= 3107) {
transferList = new Transfer[] {
HTMLTransfer.getInstance(),
URLTransfer.getInstance(),
FileTransfer.getInstance(),
TextTransfer.getInstance()
};
} else {
transferList = new Transfer[] {
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(AzureusCoreImpl.getSingleton(),
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);
}
}
}
}
}
/**
* Force label to use more vertical space if wrapped and in a GridLayout
* Place this listener on the _parent_ of the label
* See Eclipse SWT Bug #9866 (GridLayout does not handle wrapped Label properly)
* This workaround only works for labels who:
* - horizontally span their whole parent
* (ie. the parent has 3 columns, the label must span 3 columns)
* - GridData style has GridData.FILL_HORIZONTAL
* - Label style has SWT.WRAP
*
* @author TuxPaper
* @note Bug 9866 fixed in 3105 and later
*/
public static class LabelWrapControlListener
extends ControlAdapter
{
public void controlResized(ControlEvent e) {
if (SWT.getVersion() >= 3105)
return;
Composite parent = (Composite) e.widget;
Control children[] = parent.getChildren();
if (children.length > 0) {
GridLayout parentLayout = (GridLayout) parent.getLayout();
if (parentLayout != null) {
Point size;
int marginWidth = parentLayout.marginWidth;
Composite grandParent = parent.getParent();
if (grandParent instanceof ScrolledComposite) {
Composite greatGP = grandParent.getParent();
if (greatGP != null) {
size = greatGP.getSize();
if (greatGP.getLayout() instanceof GridLayout) {
marginWidth += ((GridLayout) greatGP.getLayout()).marginWidth;
}
} else {
// not tested
size = grandParent.getSize();
}
if (grandParent.getLayout() instanceof GridLayout) {
marginWidth += ((GridLayout) grandParent.getLayout()).marginWidth;
}
ScrollBar sb = grandParent.getVerticalBar();
if (sb != null) {
// I don't know why, but we have to remove one
size.x -= sb.getSize().x + 1;
}
} else
size = parent.getSize();
boolean oneChanged = false;
for (int i = 0; i < children.length; i++) {
if ((children[i] instanceof Label)
&& (children[i].getStyle() & SWT.WRAP) == SWT.WRAP) {
GridData gd = (GridData) children[i].getLayoutData();
if (gd != null && gd.horizontalAlignment == GridData.FILL) {
if (gd.horizontalSpan == parentLayout.numColumns) {
gd.widthHint = size.x - 2 * marginWidth;
oneChanged = true;
} else {
Point pt = children[i].getLocation();
gd.widthHint = size.x - pt.x - (2 * marginWidth);
oneChanged = true;
}
}
}
}
if (oneChanged) {
parent.layout(true);
if (grandParent instanceof ScrolledComposite) {
((ScrolledComposite) grandParent).setMinSize(parent.computeSize(
SWT.DEFAULT, SWT.DEFAULT, true));
}
}
}
} // size
} // controlResized
} // class
public static void alternateRowBackground(TableItem 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);
}
}
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();
int iBottomIndex = getTableBottomIndex(table, iTopIndex);
Color[] colors = {
table.getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND),
Colors.colorAltRow
};
int iFixedIndex = iTopIndex;
for (int i = iTopIndex; i <= iBottomIndex; i++) {
TableItem row = table.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)
item.setImage(ImageRepository.getImage(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) {
final String[] sImageNames = {
"azureus",
"azureus32",
"azureus64",
"azureus128"
};
if (Constants.isOSX)
return;
try {
ArrayList list = new ArrayList();
Image[] images = new Image[] {
ImageRepository.getImage("azureus"),
ImageRepository.getImage("azureus32"),
ImageRepository.getImage("azureus64"),
ImageRepository.getImage("azureus128")
};
for (int i = 0; i < images.length; i++) {
Image image = ImageRepository.getImage(sImageNames[i]);
if (image != null)
list.add(image);
}
if (list.size() == 0)
return;
shell.setImages((Image[]) list.toArray(new Image[0]));
} catch (NoSuchMethodError e) {
// SWT < 3.0
Image image = ImageRepository.getImage(sImageNames[0]);
if (image != null)
shell.setImage(image);
}
}
private 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
*/
private static boolean execSWTThread(final Runnable code, final int msLater) {
Display display = getDisplay();
if (display == null || code == null) {
return false;
}
if (msLater < 0 && display.getThread() == Thread.currentThread()) {
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());
}
}
} else if (msLater >= -1) {
try {
if (queue == null) {
if (msLater <= 0) {
display.asyncExec(code);
} else {
display.timerExec(msLater, code);
}
} else {
queue.add(code);
diag_logger.log(SystemTime.getCurrentTime() + "] + QUEUE. size= "
+ queue.size() + "; add " + code + " via "
+ Debug.getCompressedStackTrace());
final long lStart = SystemTime.getCurrentTime();
final Display fDisplay = display;
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 {
wait = SystemTime.getCurrentTime() - lStartTimeRun;
if (wait > 500) {
diag_logger.log(SystemTime.getCurrentTime() + "] took "
+ wait + "ms to run " + code);
}
diag_logger.log(SystemTime.getCurrentTime()
+ "] - QUEUE. size=" + queue.size());
queue.remove(code);
}
}
};
if (msLater <= 0) {
display.asyncExec(runnableWrapper);
} else {
display.timerExec(msLater, 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());
}
/** Open a messagebox using resource keys for title/text
*
* @param parent Parent shell for messagebox
* @param style SWT styles for messagebox
* @param keyPrefix message bundle key prefix used to get title and text.
* Title will be keyPrefix + ".title", and text will be set to
* keyPrefix + ".text"
* @param textParams any parameters for text
*
* @return what the messagebox returns
*/
public static int openMessageBox(Shell parent, int style, String keyPrefix,
String[] textParams) {
if (style == SWT.OK) {
int ret = new MessageBoxShell(parent, MessageText.getString(keyPrefix
+ ".title"), MessageText.getString(keyPrefix + ".text", textParams),
new String[] {
MessageText.getString("Button.ok")
}, 0).open();
return (ret == 0) ? SWT.OK : SWT.CANCEL;
} else {
MessageBox mb = new MessageBox(parent, style);
mb.setMessage(MessageText.getString(keyPrefix + ".text", textParams));
mb.setText(MessageText.getString(keyPrefix + ".title"));
return mb.open();
}
}
/** Open a messagebox with actual title and text
*
* @param parent
* @param style
* @param title
* @param text
* @return
*/
public static int openMessageBox(Shell parent, int style, String title,
String text) {
MessageBox mb = new MessageBox(parent == null ? findAnyShell() : parent,
style);
mb.setMessage(text);
mb.setText(title);
return mb.open();
}
/**
* Bottom Index may be negative
*/
public static int getTableBottomIndex(Table table, int iTopIndex) {
// on Linux, getItemHeight is slow AND WRONG. so is getItem(x).getBounds().y
// getItem(Point) is slow on OSX
int itemCount = table.getItemCount();
if (!table.isVisible() || iTopIndex >= itemCount)
return -1;
if (Constants.isOSX) {
try {
TableItem item = table.getItem(iTopIndex);
Rectangle bounds = item.getBounds();
Rectangle clientArea = table.getClientArea();
int itemHeight = table.getItemHeight();
int iBottomIndex = Math.min(iTopIndex
+ (clientArea.height + clientArea.y - bounds.y - 1) / itemHeight,
itemCount - 1);
// System.out.println(bounds + ";" + clientArea + ";" + itemHeight + ";bi="
// + iBottomIndex + ";ti=" + iTopIndex + ";"
// + (clientArea.height + clientArea.y - bounds.y - 1));
return iBottomIndex;
} catch (NoSuchMethodError e) {
// item.getBounds is 3.2
return Math.min(
iTopIndex
+ ((table.getClientArea().height - table.getHeaderHeight() - 1) / table.getItemHeight())
+ 1, table.getItemCount() - 1);
}
}
// getItem will return null if clientArea's height is smaller than
// header height.
int areaHeight = table.getClientArea().height;
if (areaHeight <= table.getHeaderHeight())
return -1;
// 2 offset to be on the safe side
TableItem bottomItem = table.getItem(new Point(2,
table.getClientArea().height - 1));
int iBottomIndex = (bottomItem != null) ? table.indexOf(bottomItem)
: itemCount - 1;
return iBottomIndex;
}
public static void launch(String sFile) {
if (sFile == null) {
return;
}
if (SWT.getVersion() >= 3315 || SWT.getVersion() < 3300
|| UrlUtils.isURL(sFile) || sFile.startsWith("mailto:")) {
boolean launched = Program.launch(sFile);
if (!launched && Constants.isUnix
&& (UrlUtils.isURL(sFile) || sFile.startsWith("mailto:"))) {
if (!Program.launch("xdg-open " + sFile)) {
Program.launch("htmlview " + sFile);
}
}
} else {
if (Constants.isOSX) {
Program.launch("file://" + sFile.replaceAll(" ", "%20"));
} else {
Program.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) {
if (DIRECT_SETCHECKED) {
item.setChecked(checked);
} else {
item.setChecked(!checked);
item.getDisplay().asyncExec(new AERunnable() {
public void runSupport() {
item.setChecked(checked);
}
});
}
if (Constants.isWindowsXP || isGTK) {
Rectangle r = item.getBounds(0);
Table table = item.getParent();
Rectangle rTable = table.getClientArea();
r.y += VerticalAligner.getTableAdjustVerticalBy(table);
table.redraw(0, r.y, rTable.width, r.height, true);
}
}
public static boolean linkShellMetricsToConfig(final Shell shell,
final String sConfigPrefix) {
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]);
shell.setBounds(shellBounds);
verifyShellRect(shell, true);
bDidResize = true;
}
} catch (Exception e) {
}
}
boolean isMaximized = COConfigurationManager.getBooleanParameter(sConfigPrefix
+ ".maximized")
&& !Constants.isOSX;
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()
&& !Constants.isOSX ? 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);
}
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;
}
public static int pixelsToPoint(double pixels, int dpi) {
int ret = (int) Math.round((pixels * 72.0) / dpi);
return ret;
}
public 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);
}
public 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;
}
/**
* @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()) {
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;
}
Object layoutData = control.getLayoutData();
if (layoutData instanceof FormData) {
FormData fd = (FormData) layoutData;
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 (newSize.y >= targetSize.y && 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();
}
}
});
}
/**
*
* @param baseFont
* @param gc Can be null
* @param heightInPixels
* @return
*
* @since 3.0.0.7
*/
public static int getFontHeightFromPX(Font baseFont, GC gc, int heightInPixels) {
Font font = null;
Device device = baseFont.getDevice();
// hack..
heightInPixels++;
// This isn't accurate, but gets us close
int size = Utils.pixelsToPoint(heightInPixels, device.getDPI().y) + 1;
if (size <= 0) {
return 0;
}
boolean bOurGC = gc == null || gc.isDisposed();
try {
if (bOurGC) {
gc = new GC(device);
}
FontData[] fontData = baseFont.getFontData();
do {
if (font != null) {
size--;
font.dispose();
}
fontData[0].setHeight(size);
font = new Font(device, fontData);
gc.setFont(font);
} while (font != null && gc.textExtent(GOOD_STRING).y > heightInPixels
&& size > 1);
} finally {
if (bOurGC) {
gc.dispose();
}
if (font != null && !font.isDisposed()) {
font.dispose();
}
}
return size;
}
public static int getFontHeightFromPX(Device device, FontData[] fontData,
GC gc, int heightInPixels) {
Font font = null;
// hack..
heightInPixels++;
// This isn't accurate, but gets us close
int size = Utils.pixelsToPoint(heightInPixels, device.getDPI().y) + 1;
if (size <= 0) {
return 0;
}
boolean bOurGC = gc == null || gc.isDisposed();
try {
if (bOurGC) {
gc = new GC(device);
}
do {
if (font != null) {
size--;
font.dispose();
}
fontData[0].setHeight(size);
font = new Font(device, fontData);
gc.setFont(font);
} while (font != null && gc.textExtent(GOOD_STRING).y > heightInPixels
&& size > 1);
} finally {
if (bOurGC) {
gc.dispose();
}
if (font != null && !font.isDisposed()) {
font.dispose();
}
}
return size;
}
public static Font getFontWithHeight(Font baseFont, GC gc, int heightInPixels) {
Font font = null;
Device device = baseFont.getDevice();
// hack..
heightInPixels++;
// This isn't accurate, but gets us close
int size = Utils.pixelsToPoint(heightInPixels, device.getDPI().y) + 1;
if (size <= 0) {
size = 2;
}
boolean bOurGC = gc == null || gc.isDisposed();
try {
if (bOurGC) {
gc = new GC(device);
}
FontData[] fontData = baseFont.getFontData();
do {
if (font != null) {
size--;
font.dispose();
}
fontData[0].setHeight(size);
font = new Font(device, fontData);
gc.setFont(font);
} while (font != null && gc.textExtent(GOOD_STRING).y > heightInPixels
&& size > 1);
} finally {
if (bOurGC) {
gc.dispose();
}
}
return font;
}
/**
* @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 sempaphore), 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 sempaphore), 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.release();
}
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;
}
public 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;
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;
}
srcArea.intersect(dstBounds);
// draw the image with no mask! :(
GC gc = new GC(dstImage);
try {
gc.drawImage(srcImage, srcArea.x, srcArea.y, srcArea.width,
srcArea.height, dstPos.x, dstPos.y, srcArea.width, srcArea.height);
} finally {
gc.dispose();
}
ImageData dstImageData = dstImage.getImageData();
ImageData srcImageData = srcImage.getImageData();
int yPos = dstPos.y;
for (int y = 0; y < srcArea.height; y++) {
int xPos = dstPos.x;
for (int x = 0; x < srcArea.width; x++) {
dstImageData.setAlpha(xPos, yPos, srcImageData.getAlpha(x + srcArea.x,
y + srcArea.y));
xPos++;
}
yPos++;
}
return new Image(device, dstImageData);
}
/**
*
* @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 = new Rectangle(foregroundDrawArea.x
+ foregroundOffsetOnBg.x,
foregroundDrawArea.y + foregroundOffsetOnBg.y,
foregroundDrawArea.width, foregroundDrawArea.height);
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 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();
/*
* Find the monitor that this cursorLocation 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(cursorLocation)) {
break;
}
}
if (null == monitorBounds) {
return;
}
/*
* 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 (NoSuchMethodError e) {
//Do nothing
}
}
}