Package org.eclipse.jface.text.contentassist

Source Code of org.eclipse.jface.text.contentassist.AdditionalInfoController$DefaultPresenterControlCreator

/*******************************************************************************
* Copyright (c) 2000, 2008 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
*******************************************************************************/
package org.eclipse.jface.text.contentassist;

import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;

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.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableItem;

import org.eclipse.jface.internal.text.InformationControlReplacer;
import org.eclipse.jface.text.AbstractInformationControlManager;
import org.eclipse.jface.text.AbstractReusableInformationControlCreator;
import org.eclipse.jface.text.DefaultInformationControl;
import org.eclipse.jface.text.IInformationControl;
import org.eclipse.jface.text.IInformationControlCreator;
import org.eclipse.jface.text.IInformationControlExtension3;


/**
* Displays the additional information available for a completion proposal.
*
* @since 2.0
*/
class AdditionalInfoController extends AbstractInformationControlManager {
 
  /**
   * A timer thread.
   *
   * @since 3.2
   */
  private static abstract class Timer {
    private static final int DELAY_UNTIL_JOB_IS_SCHEDULED= 50;
   
    /**
     * A <code>Task</code> is {@link Task#run() run} when {@link #delay()} milliseconds have
     * elapsed after it was scheduled without a {@link #reset(ICompletionProposal) reset}
     * to occur.
     */
    private abstract class Task implements Runnable {
      /**
       * @return the delay in milliseconds before this task should be run
       */
          public abstract long delay();
          /**
           * Runs this task.
           */
          public abstract void run();
          /**
           * @return the task to be scheduled after this task has been run
           */
          public abstract Task nextTask();
    }
   
    /**
     * IDLE: the initial task, and active whenever the info has been shown. It cannot be run,
     * but specifies an infinite delay.
     */
    private final Task IDLE= new Task() {
      public void run() {
        Assert.isTrue(false);
      }
     
      public Task nextTask() {
        Assert.isTrue(false);
          return null;
      }
     
      public long delay() {
        return Long.MAX_VALUE;
      }
     
      public String toString() {
        return "IDLE"; //$NON-NLS-1$
      }
    };
    /**
     * FIRST_WAIT: Schedules a platform {@link Job} to fetch additional info from an {@link ICompletionProposalExtension5}.
     */
    private final Task FIRST_WAIT= new Task() {
      public void run() {
        final ICompletionProposalExtension5 proposal= getCurrentProposalEx();
        Job job= new Job(JFaceTextMessages.getString("AdditionalInfoController.job_name")) { //$NON-NLS-1$
          protected IStatus run(IProgressMonitor monitor) {
            Object info;
                        try {
                          info= proposal.getAdditionalProposalInfo(monitor);
                        } catch (RuntimeException x) {
                          /*
               * XXX: This is the safest fix at this point so close to end of 3.2.
               *    Will be revisited when fixing https://bugs.eclipse.org/bugs/show_bug.cgi?id=101033
                           */
                          return new Status(IStatus.WARNING, "org.eclipse.jface.text", IStatus.OK, "", x); //$NON-NLS-1$ //$NON-NLS-2$
                        }
            setInfo((ICompletionProposal) proposal, info);
              return new Status(IStatus.OK, "org.eclipse.jface.text", IStatus.OK, "", null); //$NON-NLS-1$ //$NON-NLS-2$
          }
        };
        job.schedule();
      }
     
      public Task nextTask() {
        return SECOND_WAIT;
      }
     
      public long delay() {
        return DELAY_UNTIL_JOB_IS_SCHEDULED;
      }
     
      public String toString() {
        return "FIRST_WAIT"; //$NON-NLS-1$
      }
    };
    /**
     * SECOND_WAIT: Allows display of additional info obtained from an
     * {@link ICompletionProposalExtension5}.
     */
    private final Task SECOND_WAIT= new Task() {
      public void run() {
        // show the info
        allowShowing();
      }
     
      public Task nextTask() {
        return IDLE;
      }
     
      public long delay() {
        return fDelay - DELAY_UNTIL_JOB_IS_SCHEDULED;
      }
     
      public String toString() {
        return "SECOND_WAIT"; //$NON-NLS-1$
      }
    };
    /**
     * LEGACY_WAIT: Posts a runnable into the display thread to fetch additional info from non-{@link ICompletionProposalExtension5}s.
     */
    private final Task LEGACY_WAIT= new Task() {
      public void run() {
        final ICompletionProposal proposal= getCurrentProposal();
        if (!fDisplay.isDisposed()) {
          fDisplay.asyncExec(new Runnable() {
            public void run() {
              synchronized (Timer.this) {
                if (proposal == getCurrentProposal()) {
                  Object info= proposal.getAdditionalProposalInfo();
                  showInformation(proposal, info);
                }
              }
            }
          });
        }
      }
     
      public Task nextTask() {
        return IDLE;
      }
     
      public long delay() {
        return fDelay;
      }
     
      public String toString() {
          return "LEGACY_WAIT"; //$NON-NLS-1$
      }
    };
    /**
     * EXIT: The task that triggers termination of the timer thread.
     */
    private final Task EXIT= new Task() {
      public long delay() {
            return 1;
          }

      public Task nextTask() {
        Assert.isTrue(false);
            return EXIT;
          }

      public void run() {
        Assert.isTrue(false);
          }
     
      public String toString() {
        return "EXIT"; //$NON-NLS-1$
      }
    };
   
    /** The timer thread. */
    private final Thread fThread;

    /** The currently waiting / active task. */
    private Task fTask;
    /** The next wake up time. */
    private long fNextWakeup;
   
    private ICompletionProposal fCurrentProposal= null;
    private Object fCurrentInfo= null;
    private boolean fAllowShowing= false;

    private final Display fDisplay;
    private final int fDelay;
   
    /**
     * Creates a new timer.
     *
     * @param display the display to use for display thread posting.
     * @param delay the delay until to show additional info
     */
    public Timer(Display display, int delay) {
        fDisplay= display;
      fDelay= delay;
      long current= System.currentTimeMillis();
        schedule(IDLE, current);

        fThread= new Thread(new Runnable() {
          public void run() {
            try {
                      loop();
                    } catch (InterruptedException x) {
                    }
          }
        }, JFaceTextMessages.getString("InfoPopup.info_delay_timer_name")); //$NON-NLS-1$
      fThread.start();
    }
   
    /**
     * Terminates the timer thread.
     */
    public synchronized final void terminate() {
      schedule(EXIT, System.currentTimeMillis());
      notifyAll();
    }
   
    /**
     * Resets the timer thread as the selection has changed to a new proposal.
     *
     * @param p the new proposal
     */
    public final synchronized void reset(ICompletionProposal p) {
      if (fCurrentProposal != p) {
        fCurrentProposal= p;
        fCurrentInfo= null;
        fAllowShowing= false;

        long oldWakeup= fNextWakeup;
        Task task= taskOnReset(p);
        schedule(task, System.currentTimeMillis());
        if (fNextWakeup < oldWakeup)
          notifyAll();
      }
    }
   
        private Task taskOnReset(ICompletionProposal p) {
      if (p == null)
        return IDLE;
      if (isExt5(p))
        return FIRST_WAIT;
      return LEGACY_WAIT;
        }

        private synchronized void loop() throws InterruptedException {
          long current= System.currentTimeMillis();
          Task task= currentTask();

          while (task != EXIT) {
            long delay= fNextWakeup - current;
            if (delay <= 0) {
              task.run();
              task= task.nextTask();
              schedule(task, current);
            } else {
              wait(delay);
              current= System.currentTimeMillis();
              task= currentTask();
            }
          }
        }
       
        private Task currentTask() {
          return fTask;
        }
       
        private void schedule(Task task, long current) {
          fTask= task;
          long nextWakeup= current + task.delay();
          if (nextWakeup <= current)
            fNextWakeup= Long.MAX_VALUE;
          else
            fNextWakeup= nextWakeup;
        }

    private boolean isExt5(ICompletionProposal p) {
      return p instanceof ICompletionProposalExtension5;
    }

        ICompletionProposal getCurrentProposal() {
          return fCurrentProposal;
        }
       
        ICompletionProposalExtension5 getCurrentProposalEx() {
          Assert.isTrue(fCurrentProposal instanceof ICompletionProposalExtension5);
          return (ICompletionProposalExtension5) fCurrentProposal;
        }

        synchronized void setInfo(ICompletionProposal proposal, Object info) {
          if (proposal == fCurrentProposal) {
            fCurrentInfo= info;
            if (fAllowShowing) {
              triggerShowing();
            }
          }
        }

        private void triggerShowing() {
      final Object info= fCurrentInfo;
      if (!fDisplay.isDisposed()) {
        fDisplay.asyncExec(new Runnable() {
          public void run() {
            synchronized (Timer.this) {
              if (info == fCurrentInfo) {
                showInformation(fCurrentProposal, info);
              }
            }
          }
        });
      }
        }
       
        /**
         * Called in the display thread to show additional info.
         *
         * @param proposal the proposal to show information about
         * @param info the information about <code>proposal</code>
         */
        protected abstract void showInformation(ICompletionProposal proposal, Object info);

        void allowShowing() {
          fAllowShowing= true;
           triggerShowing();
        }
  }
  /**
   * Internal table selection listener.
   */
  private class TableSelectionListener implements SelectionListener {

    /*
     * @see SelectionListener#widgetSelected(SelectionEvent)
     */
    public void widgetSelected(SelectionEvent e) {
      handleTableSelectionChanged();
    }

    /*
     * @see SelectionListener#widgetDefaultSelected(SelectionEvent)
     */
    public void widgetDefaultSelected(SelectionEvent e) {
    }
  }
 
  /**
   * Default control creator for the information control replacer.
   * @since 3.4
   */
  private static class DefaultPresenterControlCreator extends AbstractReusableInformationControlCreator {
    public IInformationControl doCreateInformationControl(Shell shell) {
      return new DefaultInformationControl(shell, true);
    }
  }

  /** The proposal table. */
  private Table fProposalTable;
  /** The table selection listener */
  private SelectionListener fSelectionListener= new TableSelectionListener();
  /** The delay after which additional information is displayed */
  private final int fDelay;
  /**
   * The timer thread.
   * @since 3.2
   */
  private Timer fTimer;
  /**
   * The proposal most recently set by {@link #showInformation(ICompletionProposal, Object)},
   * possibly <code>null</code>.
   * @since 3.2
   */
  private ICompletionProposal fProposal;
  /**
   * The information most recently set by {@link #showInformation(ICompletionProposal, Object)},
   * possibly <code>null</code>.
   * @since 3.2
   */
  private Object fInformation;

  /**
   * 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, ANCHOR_BOTTOM });
     
      /*
     * Adjust the location by one pixel towards the proposal popup, so that the single pixel
     * border of the additional info popup overlays with the border of the popup. This avoids
     * having a double black line.
     */
      int spacing= -1;
    setMargins(spacing, spacing); // see also adjustment in #computeLocation
   
    InformationControlReplacer replacer= new InformationControlReplacer(new DefaultPresenterControlCreator());
    getInternalAccessor().setInformationControlReplacer(replacer);
  }

  /*
   * @see AbstractInformationControlManager#install(Control)
   */
  public void install(Control control) {

    if (fProposalTable == control) {
      // already installed
      return;
    }

    super.install(control.getShell());

    Assert.isTrue(control instanceof Table);
    fProposalTable= (Table) control;
    fProposalTable.addSelectionListener(fSelectionListener);
    getInternalAccessor().getInformationControlReplacer().install(fProposalTable);
   
    fTimer= new Timer(fProposalTable.getDisplay(), fDelay) {
      protected void showInformation(ICompletionProposal proposal, Object info) {
        InformationControlReplacer replacer= getInternalAccessor().getInformationControlReplacer();
        if (replacer != null)
          replacer.hideInformationControl();
        AdditionalInfoController.this.showInformation(proposal, info);
      }
    };
  }

  /*
   * @see AbstractInformationControlManager#disposeInformationControl()
   */
  public void disposeInformationControl() {

    if (fTimer !=null) {
      fTimer.terminate();
      fTimer= null;
    }

    fProposal= null;
    fInformation= null;

    if (fProposalTable != null && !fProposalTable.isDisposed()) {
      fProposalTable.removeSelectionListener(fSelectionListener);
      fProposalTable= null;
    }

    super.disposeInformationControl();
  }

  /**
   *Handles a change of the line selected in the associated selector.
   */
  public void handleTableSelectionChanged() {

    if (fProposalTable != null && !fProposalTable.isDisposed() && fProposalTable.isVisible()) {
      TableItem[] selection= fProposalTable.getSelection();
      if (selection != null && selection.length > 0) {

        TableItem item= selection[0];

        Object d= item.getData();
        if (d instanceof ICompletionProposal) {
          ICompletionProposal p= (ICompletionProposal) d;
          fTimer.reset(p);
        }
      }
    }
  }

  void showInformation(ICompletionProposal proposal, Object info) {
    if (fProposalTable == null || fProposalTable.isDisposed())
      return;
   
    if (fProposal == proposal && ((info == null && fInformation == null) || (info != null && info.equals(fInformation))))
      return;
   
        fInformation= info;
        fProposal= proposal;
        showInformation();
    }

  /*
   * @see AbstractInformationControlManager#computeInformation()
   */
  protected void computeInformation() {
    if (fProposal instanceof ICompletionProposalExtension3)
      setCustomInformationControlCreator(((ICompletionProposalExtension3) fProposal).getInformationControlCreator());
    else
      setCustomInformationControlCreator(null);

    // compute subject area
    Point size= fProposalTable.getShell().getSize();

    // set information & subject area
    setInformation(fInformation, new Rectangle(0, 0, size.x, size.y));
  }

  /*
   * @see org.eclipse.jface.text.AbstractInformationControlManager#computeLocation(org.eclipse.swt.graphics.Rectangle, org.eclipse.swt.graphics.Point, org.eclipse.jface.text.AbstractInformationControlManager.Anchor)
   */
  protected Point computeLocation(Rectangle subjectArea, Point controlSize, Anchor anchor) {
      Point location= super.computeLocation(subjectArea, controlSize, anchor);
     
      /*
     * The location is computed using subjectControl.toDisplay(), which does not include the
     * trim of the subject control. As we want the additional info popup aligned with the outer
     * coordinates of the proposal popup, adjust this here
     */
      Rectangle trim= fProposalTable.getShell().computeTrim(0, 0, 0, 0);
      location.x += trim.x;
      location.y += trim.y;

    return location;
  }

  /*
   * @see org.eclipse.jface.text.AbstractInformationControlManager#computeSizeConstraints(Control, IInformationControl)
   */
  protected Point computeSizeConstraints(Control subjectControl, IInformationControl informationControl) {
    // at least as big as the proposal table
    Point sizeConstraint= super.computeSizeConstraints(subjectControl, informationControl);
    Point size= subjectControl.getShell().getSize();
   
      // AbstractInformationControlManager#internalShowInformationControl(Rectangle, Object) adds trims
    // to the computed constraints. Need to remove them here, to make the outer bounds of the additional
    // info shell fit the bounds of the proposal shell:
      if (fInformationControl instanceof IInformationControlExtension3) {
        Rectangle shellTrim= ((IInformationControlExtension3) fInformationControl).computeTrim();
        size.x -= shellTrim.width;
        size.y -= shellTrim.height;
      }

    if (sizeConstraint.x < size.x)
      sizeConstraint.x= size.x;
    if (sizeConstraint.y < size.y)
      sizeConstraint.y= size.y;
    return sizeConstraint;
  }
 
  /*
   * @see org.eclipse.jface.text.AbstractInformationControlManager#hideInformationControl()
   */
  protected void hideInformationControl() {
    super.hideInformationControl();
  }

  /**
   * @return the current information control, or <code>null</code> if none available
   */
  public IInformationControl getCurrentInformationControl2() {
    return getInternalAccessor().getCurrentInformationControl();
  }
}

TOP

Related Classes of org.eclipse.jface.text.contentassist.AdditionalInfoController$DefaultPresenterControlCreator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.