protected void paintCaret(Graphics gfx, int line, int y) {
    // System.out.println("painting caret " + line + " " + y);
    if (textArea.isCaretVisible()) {
      // System.out.println("caret is visible");
      int offset = textArea.getCaretPosition() - textArea.getLineStartOffset(line);
      int caretX = textArea._offsetToX(line, offset);
      int caretWidth = ((blockCaret || textArea.isOverwriteEnabled()) ? fm.charWidth('w') : 1);
      y += fm.getLeading() + fm.getMaxDescent();
      int height = fm.getHeight();

      // System.out.println("caretX, width = " + caretX + " " + caretWidth);

      gfx.setColor(caretColor);

      if (textArea.isOverwriteEnabled()) {
        gfx.fillRect(caretX, y + height - 1, caretWidth, 1);

      } else {
        // some machines don't like the drawRect for the single
        // pixel caret.. this caused a lot of hell because on that
        // minority of machines, the caret wouldn't show up past
        // the first column. the fix is to use drawLine() in
        // those cases, as a workaround.
        if (caretWidth == 1) {
          gfx.drawLine(caretX, y, caretX, y + height - 1);
        } else {
          gfx.drawRect(caretX, y, caretWidth - 1, height - 1);
        }
        // gfx.drawRect(caretX, y, caretWidth, height - 1);
      }
    }
  }
  protected void paintCaret(Graphics gfx, int line, int y) {
    if (textArea.isCaretVisible()) {
      int offset = textArea.getCaretPosition() - textArea.getLineStartOffset(line);
      int caretX = textArea._offsetToX(line, offset);
      int caretWidth = ((blockCaret || textArea.isOverwriteEnabled()) ? fm.charWidth('w') : 1);
      y += fm.getLeading() + fm.getMaxDescent();
      int height = fm.getHeight();

      gfx.setColor(caretColor);

      if (textArea.isOverwriteEnabled()) {
        gfx.fillRect(caretX, y + height - 1, caretWidth, 1);
      } else {
        gfx.drawRect(caretX, y, caretWidth - 1, height - 1);
      }
    }
  }
  /**
   * Replaces the current selection with the replacement string.
   *
   * @param view The view
   * @return True if the operation was successful, false otherwise
   */
  public static boolean replace(View view) {
    // component that will parent any dialog boxes
    Component comp = SearchDialog.getSearchDialog(view);
    if (comp == null) comp = view;

    JEditTextArea textArea = view.getTextArea();

    Buffer buffer = view.getBuffer();
    if (!buffer.isEditable()) return false;

    boolean smartCaseReplace = getSmartCaseReplace();

    Selection[] selection = textArea.getSelection();
    if (selection.length == 0) {
      view.getToolkit().beep();
      return false;
    }

    record(view, "replace(view)", true, false);

    // a little hack for reverse replace and find
    int caret = textArea.getCaretPosition();
    Selection s = textArea.getSelectionAtOffset(caret);
    if (s != null) caret = s.getStart();

    try {
      buffer.beginCompoundEdit();

      SearchMatcher matcher = getSearchMatcher();
      if (matcher == null) return false;

      initReplace();

      int retVal = 0;

      for (int i = 0; i < selection.length; i++) {
        s = selection[i];

        retVal += replaceInSelection(view, textArea, buffer, matcher, smartCaseReplace, s);
      }

      if (reverse) {
        // so that Replace and Find continues from
        // the right location
        textArea.moveCaretPosition(caret);
      } else {
        s = textArea.getSelectionAtOffset(textArea.getCaretPosition());
        if (s != null) textArea.moveCaretPosition(s.getEnd());
      }

      if (!BeanShell.isScriptRunning()) {
        Object[] args = {Integer.valueOf(retVal), Integer.valueOf(1)};
        view.getStatus().setMessageAndClear(jEdit.getProperty("view.status.replace-all", args));
      }

      if (retVal == 0) {
        view.getToolkit().beep();
        return false;
      }

      return true;
    } catch (Exception e) {
      handleError(comp, e);
    } finally {
      buffer.endCompoundEdit();
    }

    return false;
  } // }}}
  /**
   * Finds the next occurrence of the search string.
   *
   * @param view The view
   * @return True if the operation was successful, false otherwise
   */
  public static boolean find(View view) {
    // component that will parent any dialog boxes
    Component comp = SearchDialog.getSearchDialog(view);
    if (comp == null || !comp.isShowing()) comp = view;

    String path = fileset.getNextFile(view, null);
    if (path == null) {
      GUIUtilities.error(comp, "empty-fileset", null);
      return false;
    }

    try {
      view.showWaitCursor();

      SearchMatcher matcher = getSearchMatcher();
      if (matcher == null) {
        view.getToolkit().beep();
        return false;
      }

      record(view, "find(view)", false, true);

      boolean repeat = false;
      loop:
      for (; ; ) {
        while (path != null) {
          Buffer buffer = jEdit.openTemporary(view, null, path, false);

          /* this is stupid and misleading.
           * but 'path' is not used anywhere except
           * the above line, and if this is done
           * after the 'continue', then we will
           * either hang, or be forced to duplicate
           * it inside the buffer == null, or add
           * a 'finally' clause. you decide which one's
           * worse. */
          if (reverse) {
            path = fileset.getPrevFile(view, path);
          } else {
            path = fileset.getNextFile(view, path);
          }

          if (buffer == null) continue loop;

          // Wait for the buffer to load
          if (!buffer.isLoaded()) VFSManager.waitForRequests();

          int start;

          if (view.getBuffer() == buffer && !repeat) {
            JEditTextArea textArea = view.getTextArea();
            Selection s = textArea.getSelectionAtOffset(textArea.getCaretPosition());
            if (s == null) start = textArea.getCaretPosition();
            else if (reverse) start = s.getStart();
            else start = s.getEnd();
          } else if (reverse) start = buffer.getLength();
          else start = 0;

          if (find(view, buffer, start, repeat, reverse)) return true;
        }

        if (repeat) {
          if (!BeanShell.isScriptRunning()) {
            view.getStatus().setMessageAndClear(jEdit.getProperty("view.status.search-not-found"));

            view.getToolkit().beep();
          }
          return false;
        }

        boolean restart;

        // if auto wrap is on, always restart search.
        // if auto wrap is off, and we're called from
        // a macro, stop search. If we're called
        // interactively, ask the user what to do.
        if (wrap) {
          if (!BeanShell.isScriptRunning()) {
            view.getStatus().setMessageAndClear(jEdit.getProperty("view.status.auto-wrap"));
            // beep if beep property set
            if (jEdit.getBooleanProperty("search.beepOnSearchAutoWrap")) {
              view.getToolkit().beep();
            }
          }
          restart = true;
        } else if (BeanShell.isScriptRunning()) {
          restart = false;
        } else {
          Integer[] args = {Integer.valueOf(reverse ? 1 : 0)};
          int result =
              GUIUtilities.confirm(
                  comp,
                  "keepsearching",
                  args,
                  JOptionPane.YES_NO_OPTION,
                  JOptionPane.QUESTION_MESSAGE);
          restart = (result == JOptionPane.YES_OPTION);
        }

        if (restart) {
          // start search from beginning
          path = fileset.getFirstFile(view);
          repeat = true;
        } else break loop;
      }
    } catch (Exception e) {
      handleError(comp, e);
    } finally {
      view.hideWaitCursor();
    }

    return false;
  } // }}}