/**
   * Creates and adds a single control at the given position
   *
   * @param offset
   * @param length
   * @return pair of style ranges that covers this embedded editor
   */
  public StyleRange[] createAndAddControl(int offset, int length) {
    StyleRange[] styles = null;
    Position pos = new Position(offset, length);
    if (!editorPositionMap.containsValue(pos)) {
      ContainedEditorManager newContainedEditor = addControl(offset, length);
      newContainedEditor.addListener(this);
      styles = createStyleRange(newContainedEditor, pos);
      newContainedEditor.registerActions(containingEditor);
      editorPositionMap.put(newContainedEditor, pos);
      ppManager.managePosition(pos);

      // add the annotation in the gutter
      EmbeddedAnnotation annotation = new EmbeddedAnnotation(newContainedEditor);
      if (newContainedEditor.editorKind() == CALExpressionEditorManager.EDITOR_KIND) {
        annotation.setType("org.openquark.cal.eclipse.embedded.expressionAnnotation");
      } else {
        annotation.setType("org.openquark.cal.eclipse.embedded.moduleAnnotation");
      }
      annotation.setText(newContainedEditor.getCalContents());

      // must create a new position otherwise the document object is tracking the same position
      // in two partitions
      annotationModel.addAnnotation(annotation, new Position(offset, length));
      //            annotationModel.collapse(annotation);
      editorAnnotationMap.put(newContainedEditor, annotation);
    } else {
      for (final ContainedEditorManager c : editorPositionMap.keySet()) {
        if (editorPositionMap.get(c).equals(pos)) {
          styles = createStyleRange(c, pos);
          break;
        }
      }
    }
    return styles;
  }
  /*
   * @see IPainter#paint(int)
   */
  public void paint(int reason) {
    if (fViewer == null) {
      return;
    }
    if (fViewer.getDocument() == null) {
      deactivate(false);
      return;
    }

    // initialization
    if (!fIsActive) {
      StyledText textWidget = fViewer.getTextWidget();
      textWidget.addLineBackgroundListener(this);
      textWidget.addPaintListener(this);
      fPositionManager.managePosition(fCurrentLine);
      fIsActive = true;
    }

    // This forces redraw of the line highlight
    if (updateHighlightLine()) {
      // clear last line
      // Fix the background colors for tokens that didn't have the same as line!
      if (isOpaque() && !fLastLine.isDeleted() && fViewer instanceof ITextViewerExtension2) {
        ITextViewerExtension2 ext = (ITextViewerExtension2) fViewer;
        try {
          ext.invalidateTextPresentation(fLastLine.getOffset(), fLastLine.getLength());
        } catch (Exception e) {
          IdeLog.logError(CommonEditorPlugin.getDefault(), e);
        }
      }
      drawHighlightLine(fLastLine);
      // draw new line
      drawHighlightLine(fCurrentLine);
    }
  }
 /** @see IPainter#deactivate */
 public final void deactivate(final boolean redraw) {
   if (mIsActive) {
     mIsActive = false;
     mTextWidget.removePaintListener(this);
     if (mPositionManager != null) {
       mPositionManager.unmanagePosition(mBracketPosition);
     }
     if (redraw) {
       handleDrawRequest(null);
     }
   }
 }
  /**
   * Updates all the cached information about the lines to be painted and to be cleared. Returns
   * <code>true</code> if the line number of the cursor line has changed.
   *
   * @return <code>true</code> if cursor line changed
   */
  private boolean updateHighlightLine() {
    try {

      IDocument document = fViewer.getDocument();
      int modelCaret = getModelCaret();
      int lineNumber = document.getLineOfOffset(modelCaret);
      Point selection = fViewer.getTextWidget().getSelectionRange();

      // redraw if the current line number is different from the last line number we painted
      // initially fLastLineNumber is -1
      if (lineNumber != fLastLineNumber
          || !overlaps(fCurrentLine, modelCaret)
          || (selection.y != 0)) {
        // Handle non-empty selections (turn off highlight line)
        if (selection.y != 0 && fLastLine.equals(fCurrentLine)) {
          if (fLastSelection.equals(selection)) // selection didn't change
          {
            return false; // don't redraw the highlight line
          }
          fLastSelection = selection;
          return true; // selection changed
        }
        fLastSelection = selection;
        // Update the current and last lines
        fLastLine.offset = fCurrentLine.offset;
        fLastLine.length = fCurrentLine.length;
        fLastLine.isDeleted = fCurrentLine.isDeleted;

        if (fCurrentLine.isDeleted) {
          fCurrentLine.isDeleted = false;
          fPositionManager.managePosition(fCurrentLine);
        }

        fCurrentLine.offset = document.getLineOffset(lineNumber);
        if (lineNumber == document.getNumberOfLines() - 1) {
          fCurrentLine.length = document.getLength() - fCurrentLine.offset;
        } else {
          fCurrentLine.length = document.getLineOffset(lineNumber + 1) - fCurrentLine.offset;
        }

        fLastLineNumber = lineNumber;
        return true;
      }
    } catch (BadLocationException e) {
    }

    return false;
  }
  public void deactivate(boolean redraw) {
    if (fIsActive) {
      fIsActive = false;

      /*
       * on turning off the feature one has to paint the currently highlighted line with the standard background
       * color
       */
      if (redraw) drawHighlightLine(fCurrentLine);

      fViewer.getTextWidget().removeLineBackgroundListener(this);
      fViewer.getTextWidget().removePaintListener(this);

      if (fPositionManager != null) fPositionManager.unmanagePosition(fCurrentLine);

      fLastLineNumber = -1;
      fCurrentLine.offset = 0;
      fCurrentLine.length = 0;
    }
  }
  /**
   * Removes the control from this manager. Unmanages this position,
   *
   * @param c the control to remove
   * @param doRemove whether or not this control should be completely removed, or just temporarily
   *     (eg- during a save)
   * @return the position of the removed control
   */
  private Position removeControl(ContainedEditorManager c, boolean doRemove) {
    Position p;
    if (doRemove) {
      p = editorPositionMap.remove(c);
    } else {
      p = editorPositionMap.get(c);
    }
    try {
      ppManager.unmanagePosition(p);

      if (doRemove && !p.isDeleted()) {
        //                Position projected = modelToProjected(p);
        //                styledText.replaceStyleRanges(projected.offset, projected.length, new
        // StyleRange[0]);
        styledText.replaceStyleRanges(p.offset, p.length, new StyleRange[0]);
      }
    } catch (NullPointerException e) {
      // do nothing...trying to unmanage posn after
      // document has already been uninstalled.
    }
    c.removeListener(this);
    c.dispose();
    return p;
  }
  /** @see IPainter#paint(int) */
  public final void paint(final int reason) {
    Point selection = mSourceViewer.getSelectedRange();
    if (selection.y > 0) {
      deactivate(true);
      return;
    }

    SexpNavigator explorer = mEditor.getExplorer();

    boolean backward = true;
    boolean closeToParen = false;
    int offset = selection.x;
    IDocument document = mEditor.getDocument();
    try {
      char previousChar = '\0';
      char nextChar = '\0';

      if (selection.x > 0) previousChar = document.getChar(selection.x - 1);

      if (selection.x > 0
          && SchemeScannerUtilities.isClosingParenthesis(previousChar)
          && SchemeTextUtilities.getPartition(document, selection.x - 1).getType()
              == IDocument.DEFAULT_CONTENT_TYPE) {
        closeToParen = true;
      } else {
        nextChar = document.getChar(selection.x);
        if (selection.x < document.getLength() - 1
            && SchemeScannerUtilities.isOpeningParenthesis(nextChar)
            && SchemeTextUtilities.getPartition(document, selection.x).getType()
                == IDocument.DEFAULT_CONTENT_TYPE) {
          closeToParen = true;
          backward = false;
        }
      }

      if (closeToParen && backward && explorer.backwardSexpression(selection.x)) {
        offset = explorer.getListStart();
        char matchingChar = document.getChar(offset);
        mMismatch =
            SchemeScannerUtilities.getParenthesisType(previousChar)
                != SchemeScannerUtilities.getParenthesisType(matchingChar);
      } else {
        if (closeToParen && !backward && explorer.forwardSexpression(selection.x)) {
          offset = explorer.getSexpEnd() - 1;
          char matchingChar = document.getChar(offset);
          mMismatch =
              SchemeScannerUtilities.getParenthesisType(nextChar)
                  != SchemeScannerUtilities.getParenthesisType(matchingChar);
        } else {
          deactivate(true);
          return;
        }
      }

    } catch (BadLocationException exception) {
      deactivate(true);
      return;
    }

    if (mIsActive) {
      // only if different
      if (offset != mBracketPosition.getOffset()) {
        // remove old highlighting
        handleDrawRequest(null);
        // update position
        mBracketPosition.isDeleted = false;
        mBracketPosition.offset = offset;
        mBracketPosition.length = 1;
        // apply new highlighting
        handleDrawRequest(null);
      }
    } else {
      mIsActive = true;

      mBracketPosition.isDeleted = false;
      mBracketPosition.offset = offset;
      mBracketPosition.length = 1;

      mTextWidget.addPaintListener(this);
      mPositionManager.managePosition(mBracketPosition);
      handleDrawRequest(null);
    }
  }