/**
   * Sets the current replacement string.
   *
   * @param replace The new replacement string
   */
  public static void setReplaceString(String replace) {
    if (replace.equals(SearchAndReplace.replace)) return;

    SearchAndReplace.replace = replace;

    EditBus.send(new SearchSettingsChanged(null));
  } // }}}
  /**
   * Sets the state of the auto wrap around flag.
   *
   * @param wrap If true, the 'continue search from start' dialog will not be displayed
   * @since jEdit 3.2pre2
   */
  public static void setAutoWrapAround(boolean wrap) {
    if (wrap == SearchAndReplace.wrap) return;

    SearchAndReplace.wrap = wrap;

    EditBus.send(new SearchSettingsChanged(null));
  } // }}}
  /**
   * Sets the state of the BeanShell replace flag.
   *
   * @param beanshell True if the replace string is a BeanShell expression
   * @since jEdit 3.2pre2
   */
  public static void setBeanShellReplace(boolean beanshell) {
    if (beanshell == SearchAndReplace.beanshell) return;

    SearchAndReplace.beanshell = beanshell;

    EditBus.send(new SearchSettingsChanged(null));
  } // }}}
  /**
   * Determines whether a reverse search will conducted from the current position to the beginning
   * of a buffer. Note that reverse search and regular expression search is mutually exclusive;
   * enabling one will disable the other.
   *
   * @param reverse True if searches should go backwards, false otherwise
   */
  public static void setReverseSearch(boolean reverse) {
    if (reverse == SearchAndReplace.reverse) return;

    SearchAndReplace.reverse = reverse;

    EditBus.send(new SearchSettingsChanged(null));
  } // }}}
  /**
   * Sets the current search string.
   *
   * @param search The new search string
   */
  public static void setSearchString(String search) {
    if (search.equals(SearchAndReplace.search)) return;

    SearchAndReplace.search = search;
    matcher = null;

    EditBus.send(new SearchSettingsChanged(null));
  } // }}}
  /**
   * Sets the ignore case flag.
   *
   * @param ignoreCase True if searches should be case insensitive, false otherwise
   */
  public static void setIgnoreCase(boolean ignoreCase) {
    if (ignoreCase == SearchAndReplace.ignoreCase) return;

    SearchAndReplace.ignoreCase = ignoreCase;
    matcher = null;

    EditBus.send(new SearchSettingsChanged(null));
  } // }}}
  /**
   * Sets the whole word flag.
   *
   * @param wholeWord True if only whole words should be searched, false otherwise
   * @since 4.5pre1
   */
  public static void setWholeWord(boolean wholeWord) {
    if (wholeWord == SearchAndReplace.wholeWord) return;

    SearchAndReplace.wholeWord = wholeWord;
    matcher = null;

    EditBus.send(new SearchSettingsChanged(null));
  } // }}}
  /**
   * Sets the state of the regular expression flag.
   *
   * @param regexp True if regular expression searches should be performed
   */
  public static void setRegexp(boolean regexp) {
    if (regexp == SearchAndReplace.regexp) return;

    SearchAndReplace.regexp = regexp;
    if (regexp && reverse) reverse = false;

    matcher = null;

    EditBus.send(new SearchSettingsChanged(null));
  } // }}}
  /**
   * Finds the next instance of the search string in the specified buffer.
   *
   * @param view The view
   * @param buffer The buffer
   * @param start Location where to start the search
   * @param firstTime See {@link
   *     SearchMatcher#nextMatch(CharSequence,boolean,boolean,boolean,boolean)}.
   * @since jEdit 4.1pre7
   */
  public static boolean find(
      View view, Buffer buffer, int start, boolean firstTime, boolean reverse) throws Exception {

    EditBus.send(new PositionChanging(view.getEditPane()));

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

    CharSequence text;
    boolean startOfLine;
    boolean endOfLine;
    if (reverse) {
      text = new ReverseCharSequence(buffer.getSegment(0, start));
      startOfLine = true;
      endOfLine = (buffer.getLineEndOffset(buffer.getLineOfOffset(start)) - 1 == start);
    } else {
      text = buffer.getSegment(start, buffer.getLength() - start);
      startOfLine = (buffer.getLineStartOffset(buffer.getLineOfOffset(start)) == start);
      endOfLine = true;
    }

    String noWordSep = buffer.getStringProperty("noWordSep");
    matcher.setNoWordSep(noWordSep);
    SearchMatcher.Match match = matcher.nextMatch(text, startOfLine, endOfLine, firstTime, reverse);
    if (match != null) {
      jEdit.commitTemporary(buffer);
      view.setBuffer(buffer, true);
      JEditTextArea textArea = view.getTextArea();

      if (reverse) {
        textArea.setSelection(new Selection.Range(start - match.end, start - match.start));
        // make sure end of match is visible
        textArea.scrollTo(start - match.start, false);
        textArea.moveCaretPosition(start - match.end);
      } else {
        textArea.setSelection(new Selection.Range(start + match.start, start + match.end));
        textArea.moveCaretPosition(start + match.end);
        // make sure start of match is visible
        textArea.scrollTo(start + match.start, false);
      }

      return true;
    } else return false;
  } // }}}
  /** Loads search and replace state from the properties. */
  public static void load() {
    search = jEdit.getProperty("search.find.value");
    replace = jEdit.getProperty("search.replace.value");
    wholeWord = jEdit.getBooleanProperty("search.wholeWord.toggle");
    ignoreCase = jEdit.getBooleanProperty("search.ignoreCase.toggle");
    regexp = jEdit.getBooleanProperty("search.regexp.toggle");
    beanshell = jEdit.getBooleanProperty("search.beanshell.toggle");
    wrap = jEdit.getBooleanProperty("search.wrap.toggle");

    fileset = new CurrentBufferSet();

    // Tags plugin likes to call this method at times other than
    // startup; so we need to fire a SearchSettingsChanged to
    // notify the search bar and so on.
    matcher = null;
    EditBus.send(new SearchSettingsChanged(null));
  } // }}}
  /**
   * Sets the current search file set.
   *
   * @param fileset The file set to perform searches in
   * @see AllBufferSet
   * @see CurrentBufferSet
   * @see DirectoryListSet
   */
  public static void setSearchFileSet(SearchFileSet fileset) {
    SearchAndReplace.fileset = fileset;

    EditBus.send(new SearchSettingsChanged(null));
  } // }}}
  /**
   * Sets a custom search string matcher. Note that calling {@link #setSearchString(String)}, {@link
   * #setWholeWord(boolean)}, {@link #setIgnoreCase(boolean)}, or {@link #setRegexp(boolean)} will
   * reset the matcher to the default.
   */
  public static void setSearchMatcher(SearchMatcher matcher) {
    SearchAndReplace.matcher = matcher;

    EditBus.send(new SearchSettingsChanged(null));
  } // }}}