/**
   * Replaces text in the specified range with the replacement string.
   *
   * @param view The view
   * @param buffer The buffer
   * @param start The start offset
   * @param end The end offset
   * @return True if the operation was successful, false otherwise
   */
  public static boolean replace(View view, Buffer buffer, int start, int end) {
    if (!buffer.isEditable()) return false;

    // component that will parent any dialog boxes
    Component comp = SearchDialog.getSearchDialog(view);
    if (comp == null) comp = view;

    boolean smartCaseReplace = getSmartCaseReplace();

    try {
      buffer.beginCompoundEdit();

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

      initReplace();

      int retVal = 0;

      retVal += _replace(view, buffer, matcher, start, end, smartCaseReplace);

      if (retVal != 0) return true;
    } catch (Exception e) {
      handleError(comp, e);
    } finally {
      buffer.endCompoundEdit();
    }

    return false;
  } // }}}
  /**
   * Replaces all occurrences of the search string with the replacement string.
   *
   * @param view The view
   * @param dontOpenChangedFiles Whether to open changed files or to autosave them quietly
   * @return the number of modified files
   */
  public static boolean replaceAll(View view, boolean dontOpenChangedFiles) {
    // component that will parent any dialog boxes
    Component comp = SearchDialog.getSearchDialog(view);
    if (comp == null) comp = view;

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

    record(view, "replaceAll(view)", true, true);

    view.showWaitCursor();

    boolean smartCaseReplace = getSmartCaseReplace();

    int fileCount = 0;
    int occurCount = 0;
    try {
      SearchMatcher matcher = getSearchMatcher();
      if (matcher == null) return false;

      initReplace();

      String path = fileset.getFirstFile(view);
      loop:
      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. */
        path = fileset.getNextFile(view, path);

        if (buffer == null) continue loop;

        // Wait for buffer to finish loading
        if (buffer.isPerformingIO()) VFSManager.waitForRequests();

        if (!buffer.isEditable()) continue loop;

        // Leave buffer in a consistent state if
        // an error occurs
        int retVal = 0;

        try {
          buffer.beginCompoundEdit();
          retVal = _replace(view, buffer, matcher, 0, buffer.getLength(), smartCaseReplace);
        } finally {
          buffer.endCompoundEdit();
        }

        if (retVal != 0) {
          fileCount++;
          occurCount += retVal;
          if (dontOpenChangedFiles) {
            buffer.save(null, null);
          } else {
            jEdit.commitTemporary(buffer);
            jEdit.getBufferSetManager().addBuffer(view, buffer);
          }
        }
      }
    } catch (Exception e) {
      handleError(comp, e);
    } finally {
      view.hideWaitCursor();
    }

    /* Don't do this when playing a macro, cos it's annoying */
    if (!BeanShell.isScriptRunning()) {
      Object[] args = {Integer.valueOf(occurCount), Integer.valueOf(fileCount)};
      view.getStatus().setMessageAndClear(jEdit.getProperty("view.status.replace-all", args));
      if (occurCount == 0) view.getToolkit().beep();
    }

    return (fileCount != 0);
  } // }}}
  /**
   * 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;
  } // }}}