/** * 920 Text Editor is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * 920 Text Editor is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Foobar. If not, see <http://www.gnu.org/licenses/>. */ package com.jecelyin.editor; import android.app.ProgressDialog; import android.content.DialogInterface; import android.os.AsyncTask; import android.text.Editable; import android.widget.Toast; import java.util.ArrayList; import java.util.regex.Matcher; import java.util.regex.Pattern; import com.jecelyin.util.JecLog; public class AsyncSearch { private Pattern mPattern = null; private String mKeyword = ""; private JecEditor mJecEditor; private ArrayList<int[]> mData = new ArrayList<int[]>(); private int start = 0; private boolean next = true; private boolean replaceAll = false; private CharSequence replaceText = ""; public boolean regex = false; public boolean ignorecase = false; public void search(String keyword, boolean next, JecEditor mJecEditor) { replaceAll = false; mJecEditor.getEditText().requestFocus(); this.mJecEditor = mJecEditor; this.mKeyword = !regex ? escapeMetaChar(keyword) : keyword; if(!this.compile()) return; this.next = next; this.start = next ? mJecEditor.getEditText().getSelectionEnd() : mJecEditor.getEditText().getSelectionStart(); // 光标位置 mData.clear(); SearchTask mSearchTask = new SearchTask(); mSearchTask.execute(); } private boolean compile() { try { if (ignorecase) { this.mPattern = Pattern.compile(mKeyword, Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE | Pattern.MULTILINE); } else { this.mPattern = Pattern.compile(mKeyword); } return true; } catch (Exception e) { JecLog.msg(mJecEditor.getString(R.string.error_accurs) + e.getMessage(), e); return false; } } public void replace(String word) { if (mData.size() == 0) return; int[] ret = mData.get(0); try { mJecEditor.getEditText().getEditableText() .replace(ret[0], ret[1], (CharSequence) word); } catch (Exception e) { JecLog.msg(mJecEditor.getString(R.string.error_accurs) + e.getMessage(), e); } } public void replaceAll(String searchText, String replaceText, JecEditor mJecEditor) { this.replaceAll = true; this.replaceText = (CharSequence) replaceText; this.mJecEditor = mJecEditor; this.next = true; this.start = 0; // 光标位置 this.mKeyword = !regex ? escapeMetaChar(searchText) : searchText; if(!this.compile())return; mData.clear(); SearchTask mSearchTask = new SearchTask(); mSearchTask.execute(); } private void onSearchFinished(ArrayList<int[]> data) { if (data.size() == 0) { String msg; if (replaceAll) { msg = mJecEditor.getString(R.string.replace_finish); } else { msg = mJecEditor.getString( next ? R.string.not_found_next : R.string.not_found_up) .replaceAll("%s", mKeyword); } Toast.makeText(mJecEditor.getApplicationContext(), msg, Toast.LENGTH_LONG).show(); } else if (replaceAll) { Editable mText = mJecEditor.getEditText().getEditableText(); // 一定要从后面开始替换,不然会有问题 int end = data.size(); int[] ret; for (int i = end - 1; i >= 0; i--) { ret = data.get(i); mText.replace(ret[0], ret[1], replaceText); } } else { int[] ret = data.get(0); // 滚动当前找到的内容到中央,不然在底部看得不爽 // int end = mJecEditor.text_content.getText().length(); /* * if(ret[1]+200 <= end) { * mJecEditor.text_content.setSelection(ret[0], ret[1]+200); } else * { mJecEditor.text_content.setSelection(ret[0], end); } */ mJecEditor.getEditText().setSelection(ret[0], ret[1]); int x = mJecEditor.getEditText().getScrollX(); int y = mJecEditor.getEditText().getScrollY(); mJecEditor.getEditText().scrollBy(x, y + 40); } } private static String escapeMetaChar(String pattern) { final String metachar = ".^$[]*+?|()\\{}"; StringBuilder newpat = new StringBuilder(); int len = pattern.length(); for (int i = 0; i < len; i++) { char c = pattern.charAt(i); if (metachar.indexOf(c) >= 0) { newpat.append('\\'); } newpat.append(c); } return newpat.toString(); } private class SearchTask extends AsyncTask<String, Boolean, Boolean> { private ProgressDialog mProgressDialog; private boolean mCancelled; @Override protected void onPreExecute() { mCancelled = false; mProgressDialog = new ProgressDialog(mJecEditor); mProgressDialog.setTitle(R.string.spinner_message); mProgressDialog.setMessage(mJecEditor.getText(R.string.searching)); mProgressDialog.setIndeterminate(true); mProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER); mProgressDialog.setCancelable(true); mProgressDialog .setOnCancelListener(new DialogInterface.OnCancelListener() { public void onCancel(DialogInterface dialog) { mCancelled = true; cancel(false); } }); mProgressDialog.show(); } @Override protected Boolean doInBackground(String... params) { if (isCancelled()) { return true; } Matcher m = mPattern.matcher(mJecEditor.getEditText().getString()); if (replaceAll) { while (m.find()) { if (mCancelled) { break; } mData.add(new int[] { m.start(), m.end() }); } } else if (next) { if (m.find(start)) { mData.add(new int[] { m.start(), m.end() }); } } else { // 查找上一个 if (start <= 0) return true; // 从头开始搜索获取所有位置 while (m.find()) { if (mCancelled) { break; } else if (m.end() >= start) { if (mData.size() > 0) // fixed: Caused by: // java.lang.ArrayIndexOutOfBoundsException { int[] ret = mData.get(mData.size() - 1); // 考虑到会边搜索边修改的情况,所以只能每次都穷搜一下 mData.clear(); mData.add(ret); } break; } mData.add(new int[] { m.start(), m.end() }); } } return true; } @Override protected void onPostExecute(Boolean result) { mProgressDialog.dismiss(); mProgressDialog = null; AsyncSearch.this.onSearchFinished(mData); } @Override protected void onCancelled() { super.onCancelled(); onPostExecute(false); } } }