/**
  * Constructs a new instance of this popup, specifying the control for which this popup is showing
  * content, and how the proposals should be obtained and displayed.
  *
  * @param infoText Text to be shown in a lower info area, or <code>null</code> if there is no info
  *     area.
  */
 ContentProposalPopup(
     ContentProposalAdapter adapter,
     String infoText,
     ContentProposalList proposalList,
     int maxDisplay) {
   // IMPORTANT: Use of SWT.ON_TOP is critical here for ensuring
   // that the target control retains focus on Mac and Linux. Without
   // it, the focus will disappear, keystrokes will not go to the
   // popup, and the popup closer will wrongly close the popup.
   // On platforms where SWT.ON_TOP overrides SWT.RESIZE, we will live with this.
   // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=126138
   super(
       adapter.getControl().getShell(),
       SWT.RESIZE | SWT.ON_TOP,
       false,
       false,
       false,
       false,
       false,
       null,
       infoText);
   this.adapter = adapter;
   this.control = adapter.getControl();
   this.labelProvider = adapter.getLabelProvider();
   this.proposalList = proposalList;
 }
 /*
  * In an async block, request the proposals. This is used when clients are
  * in the middle of processing an event that affects the widget content. By
  * using an async, we ensure that the widget content is up to date with the
  * event.
  */
 private void asyncRecomputeProposals() {
   footer.setText("Searching...");
   if (isValid()) {
     control
         .getDisplay()
         .asyncExec(
             new Runnable() {
               public void run() {
                 adapter.recordCursorPosition();
                 adapter.getProposals(
                     new IContentProposalSearchHandler() {
                       @Override
                       public void handleResult(ContentProposalList proposalList) {
                         recomputeProposals(proposalList);
                       }
                     });
               }
             });
   } else {
     adapter.getProposals(
         new IContentProposalSearchHandler() {
           @Override
           public void handleResult(ContentProposalList proposalList) {
             recomputeProposals(proposalList);
           }
         });
   }
 }
  /*
   * (non-Javadoc)
   *
   * @see org.eclipse.jface.dialogs.PopupDialog.adjustBounds()
   */
  protected void adjustBounds() {
    // Get our control's location in display coordinates.
    Point location = control.getDisplay().map(control.getParent(), null, control.getLocation());
    int initialX = location.x + POPUP_OFFSET;
    int initialY = location.y + control.getSize().y + POPUP_OFFSET;
    // If we are inserting content, use the cursor position to
    // position the control.
    if (adapter.getProposalAcceptanceStyle() == ContentProposalAdapter.PROPOSAL_INSERT) {
      Rectangle insertionBounds = adapter.getControlContentAdapter().getInsertionBounds(control);
      initialX = initialX + insertionBounds.x;
      initialY = location.y + insertionBounds.y + insertionBounds.height;
    }

    // If there is no specified size, force it by setting
    // up a layout on the table.
    if (popupSize == null) {
      GridData data = new GridData(GridData.FILL_BOTH);
      data.heightHint = proposalTable.getItemHeight() * POPUP_CHAR_HEIGHT;
      data.widthHint = Math.max(control.getSize().x, POPUP_MINIMUM_WIDTH);
      proposalTable.setLayoutData(data);
      getShell().pack();
      popupSize = getShell().getSize();
    }

    // Constrain to the display
    Rectangle constrainedBounds =
        getConstrainedShellBounds(new Rectangle(initialX, initialY, popupSize.x, popupSize.y));

    // If there has been an adjustment causing the popup to overlap
    // with the control, then put the popup above the control.
    if (constrainedBounds.y < initialY)
      getShell().setBounds(initialX, location.y - popupSize.y, popupSize.x, popupSize.y);
    else getShell().setBounds(initialX, initialY, popupSize.x, popupSize.y);

    // Now set up a listener to monitor any changes in size.
    getShell()
        .addListener(
            SWT.Resize,
            new Listener() {
              public void handleEvent(Event e) {
                popupSize = getShell().getSize();
                if (infoPopup != null) {
                  infoPopup.adjustBounds();
                }
              }
            });
  }
 /**
  * Closes this popup. This method is extended to remove the control listener.
  *
  * @return <code>true</code> if the window is (or was already) closed, and <code>false</code> if
  *     it is still open
  */
 public boolean close() {
   popupCloser.removeListeners();
   if (infoPopup != null) {
     infoPopup.close();
   }
   boolean ret = super.close();
   adapter.notifyPopupClosed();
   return ret;
 }
 /*
  * Accept the current proposal.
  */
 private void acceptCurrentProposal() {
   // Close before accepting the proposal. This is important
   // so that the cursor position can be properly restored at
   // acceptance, which does not work without focus on some controls.
   // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=127108
   IContentProposal proposal = getSelectedProposal();
   close();
   if (proposal != null) {
     adapter.proposalAccepted(proposal);
   }
 }