/**
* Copyright (c) 2005-2006 Aptana, Inc.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html. If redistributing this code,
* this entire header must remain intact.
*/
package com.aptana.ide.editors.unified.contentassist;
/***********************************************************************************************************************
* Copyright (c) 2000, 2005 IBM Corporation and others. All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 which accompanies this distribution, and is
* available at http://www.eclipse.org/legal/epl-v10.html Contributors: IBM Corporation - initial API and implementation
**********************************************************************************************************************/
import org.eclipse.jface.text.Assert;
import org.eclipse.jface.text.IInformationControl;
import org.eclipse.jface.text.IInformationControlCreator;
import org.eclipse.jface.text.IInformationControlExtension3;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
import org.eclipse.jface.text.contentassist.ICompletionProposalExtension3;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableItem;
import com.aptana.ide.core.ui.CoreUIUtils;
/**
* Displays the additional information available for a completion proposal.
*
* @since 2.0
*/
class AdditionalInfoController extends AbstractInformationControlManager implements Runnable
{
/**
* Internal table selection listener.
*/
private class TableSelectionListener implements SelectionListener
{
/**
* @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent)
*/
public void widgetSelected(SelectionEvent e)
{
handleTableSelectionChanged();
}
/**
* @see org.eclipse.swt.events.SelectionListener#widgetDefaultSelected(org.eclipse.swt.events.SelectionEvent)
*/
public void widgetDefaultSelected(SelectionEvent e)
{
}
}
/** The proposal table. */
private Table fProposalTable;
/** The thread controlling the delayed display of the additional info. */
private Thread fThread;
/** Indicates whether the display delay has been reset. */
private boolean fIsReset = false;
/** Object to synchronize display thread and table selection changes. */
private final Object fMutex = new Object();
/**
* Thread access lock. since 3.0
*/
private final Object fThreadAccess = new Object();
/** Object to synchronize initial display of additional info */
private Object fStartSignal;
/** The table selection listener */
private SelectionListener fSelectionListener = new TableSelectionListener();
/** The delay after which additional information is displayed */
private int fDelay;
/**
* Creates a new additional information controller.
*
* @param creator
* the information control creator to be used by this controller
* @param delay
* time in milliseconds after which additional info should be displayed
*/
AdditionalInfoController(IInformationControlCreator creator, int delay)
{
super(creator);
fDelay = delay;
setAnchor(ANCHOR_RIGHT);
setFallbackAnchors(new Anchor[] { ANCHOR_RIGHT, ANCHOR_LEFT });
}
/**
* @see com.aptana.ide.editors.unified.contentassist.AbstractInformationControlManager#install(org.eclipse.swt.widgets.Control)
*/
public void install(Control control)
{
if (fProposalTable == control)
{
// already installed
return;
}
super.install(control);
Assert.isTrue(control instanceof Table);
fProposalTable = (Table) control;
fProposalTable.addSelectionListener(fSelectionListener);
synchronized (fThreadAccess)
{
if (fThread != null)
{
fThread.interrupt();
}
fThread = new Thread(this, "Aptana: InfoPopup.info_delay_timer_name"); //$NON-NLS-1$
fStartSignal = new Object();
synchronized (fStartSignal)
{
fThread.start();
try
{
// wait until thread is ready
fStartSignal.wait();
}
catch (InterruptedException x)
{
}
}
}
}
/**
* @see com.aptana.ide.editors.unified.contentassist.AbstractInformationControlManager#disposeInformationControl()
*/
public void disposeInformationControl()
{
synchronized (fThreadAccess)
{
if (fThread != null)
{
fThread.interrupt();
fThread = null;
}
}
if (fProposalTable != null && !fProposalTable.isDisposed())
{
fProposalTable.removeSelectionListener(fSelectionListener);
fProposalTable = null;
}
super.disposeInformationControl();
}
/**
* @see java.lang.Runnable#run()
*/
public void run()
{
try
{
while (true)
{
synchronized (fMutex)
{
if (fStartSignal != null)
{
synchronized (fStartSignal)
{
fStartSignal.notifyAll();
fStartSignal = null;
}
}
// Wait for a selection event to occur.
fMutex.wait();
while (true)
{
fIsReset = false;
// Delay before showing the popup.
fMutex.wait(fDelay);
if (!fIsReset)
{
break;
}
}
}
if (fProposalTable != null && !fProposalTable.isDisposed())
{
fProposalTable.getDisplay().asyncExec(new Runnable()
{
public void run()
{
if (!fIsReset)
{
showInformation();
}
}
});
}
}
}
catch (InterruptedException e)
{
}
synchronized (fThreadAccess)
{
// only null fThread if it is us!
if (Thread.currentThread() == fThread)
{
fThread = null;
}
}
}
/**
* Handles a change of the line selected in the associated selector.
*/
public void handleTableSelectionChanged()
{
if (fProposalTable != null && !fProposalTable.isDisposed() && fProposalTable.isVisible())
{
synchronized (fMutex)
{
fIsReset = true;
fMutex.notifyAll();
}
}
}
/**
* @see com.aptana.ide.editors.unified.contentassist.AbstractInformationControlManager#computeInformation()
*/
protected void computeInformation()
{
if (fProposalTable == null || fProposalTable.isDisposed())
{
return;
}
TableItem[] selection = fProposalTable.getSelection();
if (selection != null && selection.length > 0)
{
TableItem item = selection[0];
// compute information
String information = null;
Object d = item.getData();
if (d instanceof ICompletionProposal)
{
ICompletionProposal p = (ICompletionProposal) d;
information = p.getAdditionalProposalInfo();
}
if (d instanceof ICompletionProposalExtension3)
{
setCustomInformationControlCreator(((ICompletionProposalExtension3) d).getInformationControlCreator());
}
else
{
setCustomInformationControlCreator(null);
}
// compute subject area
setMargins(4, 4);
Rectangle area = fProposalTable.getBounds();
area.x = 0; // subject area is the whole subject control
area.y = 0;
Rectangle parentBounds = fProposalTable.getParent().getBounds();
Rectangle itemBounds = item.getBounds(0);
int verticalOffset = parentBounds.y + itemBounds.y;
// The SWT/Cocoa bug ( https://bugs.eclipse.org/bugs/show_bug.cgi?id=275617 ) causes problems
// in computation of the y offset of the information pop-up window for alignment with
// the selected completion item. Disable the smart positioning behavior for macosx/cocoa.
if (CoreUIUtils.onCocoa) {
verticalOffset = parentBounds.y;
}
// set information & subject area
setInformation(information, area, verticalOffset);
}
}
/**
* @see com.aptana.ide.editors.unified.contentassist.AbstractInformationControlManager#computeSizeConstraints(org.eclipse.swt.widgets.Control,
* org.eclipse.jface.text.IInformationControl)
*/
protected Point computeSizeConstraints(Control subjectControl, IInformationControl informationControl)
{
Point sizeConstraint = super.computeSizeConstraints(subjectControl, informationControl);
Point size = subjectControl.getSize();
Rectangle otherTrim = subjectControl.getShell().computeTrim(0, 0, 0, 0);
size.x += otherTrim.width;
size.y += otherTrim.height;
if (informationControl instanceof IInformationControlExtension3)
{
Rectangle thisTrim = ((IInformationControlExtension3) informationControl).computeTrim();
size.x -= thisTrim.width;
size.y -= thisTrim.height;
}
if (sizeConstraint.x < size.x)
{
sizeConstraint.x = size.x;
}
if (sizeConstraint.y < size.y)
{
sizeConstraint.y = size.y;
}
return sizeConstraint;
}
}