/**************************************************************************
OmegaT - Computer Assisted Translation (CAT) tool
with fuzzy matching, translation memory, keyword search,
glossaries, and translation leveraging into updated projects.
Copyright (C) 2000-2006 Keith Godfrey and Maxym Mykhalchuk
2006 Henry Pijffers
2009 Didier Briel
2010 Martin Fleurke, Antonio Vilei, Didier Briel
2012 Didier Briel
2013 Aaron Madlon-Kay, Alex Buloichik
2014 Aaron Madlon-Kay, Piotr Kulik
2015 Yu Tang, Aaron Madlon-Kay, Hiroshi Miura
Home page: http://www.omegat.org/
Support center: http://groups.yahoo.com/group/OmegaT/
This file is part of OmegaT.
OmegaT 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.
OmegaT 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 this program. If not, see <http://www.gnu.org/licenses/>.
**************************************************************************/
package org.omegat.gui.search;
import java.awt.Component;
import java.awt.Container;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.io.File;
import java.text.MessageFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import javax.swing.AbstractAction;
import javax.swing.DefaultComboBoxModel;
import javax.swing.InputMap;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.SpinnerDateModel;
import javax.swing.SpinnerNumberModel;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.undo.UndoManager;
import org.omegat.core.Core;
import org.omegat.core.data.SourceTextEntry;
import org.omegat.core.search.SearchExpression;
import org.omegat.core.search.SearchMode;
import org.omegat.core.search.Searcher;
import org.omegat.core.threads.SearchThread;
import org.omegat.gui.editor.EditorController;
import org.omegat.gui.editor.IEditor;
import org.omegat.gui.editor.IEditor.CaretPosition;
import org.omegat.gui.editor.IEditorFilter;
import org.omegat.gui.editor.filter.ReplaceFilter;
import org.omegat.gui.editor.filter.SearchFilter;
import org.omegat.util.Log;
import org.omegat.util.OConsts;
import org.omegat.util.OStrings;
import org.omegat.util.Platform;
import org.omegat.util.Preferences;
import org.omegat.util.StringUtil;
import org.omegat.util.gui.OSXIntegration;
import org.omegat.util.gui.OmegaTFileChooser;
import org.omegat.util.gui.StaticUIUtils;
import org.omegat.util.gui.UIThreadsUtil;
import org.openide.awt.Mnemonics;
/**
* This is a window that appears when user'd like to search for something. For
* each new user's request new window is created. Actual search is done by
* SearchThread.
*
* @author Keith Godfrey
* @author Henry Pijffers (henry.pijffers@saxnot.com)
* @author Didier Briel
* @author Martin Fleurke
* @author Antonio Vilei
* @author Aaron Madlon-Kay
* @author Alex Buloichik (alex73mail@gmail.com)
* @author Piotr Kulik
* @author Hiroshi Miura
*/
@SuppressWarnings("serial")
public class SearchWindowController {
private final SearchWindowForm form;
private final SearchMode mode;
private final int initialEntry;
private final CaretPosition initialCaret;
public SearchWindowController(SearchMode mode) {
form = new SearchWindowForm();
form.setJMenuBar(new SearchWindowMenu(form, this));
this.mode = mode;
initialEntry = Core.getEditor().getCurrentEntryNumber();
initialCaret = getCurrentPositionInEntryTranslationInEditor(Core.getEditor());
if (Platform.isMacOSX()) {
OSXIntegration.enableFullScreen(form);
}
dateFormat = new SimpleDateFormat(SAVED_DATE_FORMAT);
form.m_searchField.setModel(new DefaultComboBoxModel<>(HistoryManager.getSearchItems()));
if (form.m_searchField.getModel().getSize() > 0) {
form.m_searchField.setSelectedIndex(-1);
}
form.m_replaceField.setModel(new DefaultComboBoxModel<>(HistoryManager.getReplaceItems()));
if (form.m_replaceField.getModel().getSize() > 0) {
form.m_replaceField.setSelectedIndex(-1);
}
// box DateBox
Calendar calendar = Calendar.getInstance();
Date initDate = calendar.getTime();
calendar.add(Calendar.YEAR, -100);
Date earliestDate = calendar.getTime();
Date latestDate = initDate;
dateFromModel = new SpinnerDateModel(initDate, earliestDate, latestDate, Calendar.YEAR);
form.m_dateFromSpinner.setModel(dateFromModel);
dateToModel = new SpinnerDateModel(initDate, earliestDate, latestDate, Calendar.YEAR);
form.m_dateToSpinner.setModel(dateToModel);
// Box Number of results
SpinnerNumberModel numberModel = new SpinnerNumberModel(OConsts.ST_MAX_SEARCH_RESULTS, 1, Integer.MAX_VALUE,
1);
form.m_numberOfResults.setModel(numberModel);
loadPreferences();
if (!Core.getProject().isProjectLoaded()) {
// restrict user to file only access
form.m_rbDir.setSelected(true);
form.m_rbProject.setEnabled(false);
} else {
form.m_rbProject.setSelected(true);
}
// update enabled/selected status of options
updateOptionStatus();
initActions();
switch (mode) {
case SEARCH:
form.setTitle(OStrings.getString("SW_TITLE"));
form.m_replaceLabel.setVisible(false);
form.m_replaceField.setVisible(false);
form.m_replaceAllButton.setVisible(false);
form.m_replaceButton.setVisible(false);
form.m_panelSearch.setVisible(true);
form.m_panelReplace.setVisible(false);
break;
case REPLACE:
form.setTitle(OStrings.getString("SW_TITLE_REPLACE"));
form.m_SearchInPane.setVisible(false);
form.m_allResultsCB.setVisible(false);
form.m_fileNamesCB.setVisible(false);
form.m_filterButton.setVisible(false);
form.m_numberLabel.setVisible(false);
form.m_numberOfResults.setVisible(false);
form.m_panelSearch.setVisible(false);
form.m_panelReplace.setVisible(true);
form.m_excludeOrphans.setVisible(false);
break;
}
}
public SearchMode getMode() {
return mode;
}
final void initActions() {
// ///////////////////////////////////
// action listeners
form.m_dismissButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
doCancel();
}
});
form.m_filterButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
doFilter();
}
});
form.m_replaceButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
doReplace();
}
});
form.m_replaceAllButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
doReplaceAll();
}
});
form.m_searchButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
doSearch();
}
});
form.m_advancedButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
setAdvancedOptionsVisible(!form.m_advancedVisiblePane.isVisible());
}
});
form.m_authorCB.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
enableDisableAuthor();
}
});
form.m_authorField.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
doSearch();
}
});
form.m_dateToCB.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
enableDisableDateTo();
}
});
form.m_dateToButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
doResetDateTo();
}
});
form.m_dateFromButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
doResetDateFrom();
}
});
form.m_dateFromCB.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
enableDisableDateFrom();
}
});
form.m_dirButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
doBrowseDirectory();
}
});
StaticUIUtils.setEscapeAction(form, new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
doCancel();
}
});
// Set search and replace combo boxes' actions, undo, key handling
configureHistoryComboBox(form.m_searchField);
configureHistoryComboBox(form.m_replaceField);
// need to control check boxes and radio buttons manually
//
// keyword search can only be used when searching current project
// TM search only works with exact/regex search on current project
// file search only works with exact/regex search
//
// keep track of settings and only show what are valid choices
form.m_searchExactSearchRB.addActionListener(searchFieldRequestFocus);
form.m_searchKeywordSearchRB.addActionListener(searchFieldRequestFocus);
form.m_searchRegexpSearchRB.addActionListener(searchFieldRequestFocus);
form.m_searchCase.addActionListener(searchFieldRequestFocus);
form.m_searchSpaceMatchNbsp.addActionListener(searchFieldRequestFocus);
form.m_searchSource.addActionListener(searchFieldRequestFocus);
form.m_searchTranslation.addActionListener(searchFieldRequestFocus);
form.m_searchTranslatedUntranslated.addActionListener(searchFieldRequestFocus);
form.m_searchTranslated.addActionListener(searchFieldRequestFocus);
form.m_searchUntranslated.addActionListener(searchFieldRequestFocus);
form.m_searchNotesCB.addActionListener(searchFieldRequestFocus);
form.m_searchCommentsCB.addActionListener(searchFieldRequestFocus);
form.m_cbSearchInGlossaries.addActionListener(searchFieldRequestFocus);
form.m_cbSearchInMemory.addActionListener(searchFieldRequestFocus);
form.m_cbSearchInTMs.addActionListener(searchFieldRequestFocus);
form.m_allResultsCB.addActionListener(searchFieldRequestFocus);
form.m_fileNamesCB.addActionListener(searchFieldRequestFocus);
form.m_autoSyncWithEditor.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// update auto-sync flag in EntryListPane
EntryListPane viewer = (EntryListPane) form.m_viewer;
viewer.setAutoSyncWithEditor(form.m_autoSyncWithEditor.isSelected());
}
});
form.m_rbDir.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
updateOptionStatus();
// move focus to dir edit field if dir search is selected
// otherwise move focus to search field
if (form.m_rbDir.isSelected()) {
form.m_dirField.requestFocus();
} else {
form.m_searchField.requestFocus();
}
}
});
form.m_rbProject.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
updateOptionStatus();
form.m_searchField.requestFocus();
}
});
form.m_numberOfResults.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
// move focus to search edit field
form.m_searchField.requestFocus();
}
});
form.addWindowListener(new WindowAdapter() {
@Override
public void windowClosed(WindowEvent e) {
// save user preferences
savePreferences();
if (thread != null) {
thread.fin();
}
// back to the initial segment
int currentEntry = Core.getEditor().getCurrentEntryNumber();
if (initialEntry > 0 && form.m_backToInitialSegment.isSelected() && initialEntry != currentEntry) {
boolean isSegDisplayed = isSegmentDisplayed(initialEntry);
if (isSegDisplayed) {
// Restore caretPosition too
((EditorController) Core.getEditor()).gotoEntry(initialEntry, initialCaret);
} else {
// The segment is not displayed (maybe filter on). Ignore caretPosition.
Core.getEditor().gotoEntry(initialEntry);
}
}
}
});
}
private void configureHistoryComboBox(final JComboBox<String> box) {
final JTextField field = (JTextField) box.getEditor().getEditorComponent();
InputMap map = field.getInputMap();
final UndoManager undoManager = new UndoManager();
field.getDocument().addUndoableEditListener(undoManager);
// Set up undo/redo handling
KeyStroke undoKey = KeyStroke.getKeyStroke(KeyEvent.VK_Z,
Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(), false);
map.put(undoKey, new AbstractAction() {
public void actionPerformed(ActionEvent e) {
if (undoManager.canUndo()) {
undoManager.undo();
}
}
});
KeyStroke redoKey = KeyStroke.getKeyStroke(KeyEvent.VK_Y,
Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(), false);
map.put(redoKey, new AbstractAction() {
public void actionPerformed(ActionEvent e) {
if (undoManager.canRedo()) {
undoManager.redo();
}
}
});
// Close dialog with Esc key
map.put(KeyStroke.getKeyStroke("ESCAPE"), new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
if (box.isPopupVisible()) {
box.hidePopup();
} else {
doCancel();
}
}
});
// Perform search on Enter key (if search field not empty)
field.setAction(new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
if (!form.m_searchField.getEditor().getItem().toString().isEmpty()) {
doSearch();
}
}
});
}
ActionListener searchFieldRequestFocus = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// move focus to search edit field
form.m_searchField.requestFocus();
}
};
/**
* Loads the position and size of the search window and the button selection
* state.
*/
private void loadPreferences() {
// set default size and position
form.setSize(800, 700);
StaticUIUtils.persistGeometry(form, Preferences.SEARCHWINDOW_GEOMETRY_PREFIX);
// search dir options
if (Preferences.isPreferenceDefault(Preferences.SEARCHWINDOW_SEARCH_FILES, false)) {
form.m_rbDir.setSelected(true);
} else {
form.m_rbProject.setSelected(true);
}
form.m_dirField.setText(Preferences.getPreferenceDefault(Preferences.SEARCHWINDOW_DIR, ""));
form.m_recursiveCB.setSelected(Preferences.isPreferenceDefault(Preferences.SEARCHWINDOW_RECURSIVE, true));
// search type
SearchExpression.SearchExpressionType searchType = Preferences.getPreferenceEnumDefault(
Preferences.SEARCHWINDOW_SEARCH_TYPE,
SearchExpression.SearchExpressionType.EXACT);
switch (searchType) {
case EXACT:
default:
form.m_searchExactSearchRB.setSelected(true);
break;
case KEYWORD:
form.m_searchKeywordSearchRB.setSelected(true);
break;
case REGEXP:
form.m_searchRegexpSearchRB.setSelected(true);
break;
}
// case sensitivity
form.m_searchCase.setSelected(Preferences.isPreferenceDefault(
Preferences.SEARCHWINDOW_CASE_SENSITIVE, false));
// nbsp as space
form.m_searchSpaceMatchNbsp.setSelected(Preferences.isPreferenceDefault(
Preferences.SEARCHWINDOW_SPACE_MATCH_NBSP, false));
// search source
form.m_searchSource.setSelected(Preferences.isPreferenceDefault(
Preferences.SEARCHWINDOW_SEARCH_SOURCE, true));
form.m_searchTranslation.setSelected(Preferences.isPreferenceDefault(
Preferences.SEARCHWINDOW_SEARCH_TRANSLATION, true));
SearchExpression.SearchState searchState = SearchExpression.SearchState.valueOf(Preferences
.getPreferenceEnumDefault(Preferences.SEARCHWINDOW_SEARCH_STATE,
SearchExpression.SearchState.TRANSLATED_UNTRANSLATED).name());
switch (searchState) {
case TRANSLATED_UNTRANSLATED:
default:
form.m_searchTranslatedUntranslated.setSelected(true);
break;
case TRANSLATED:
form.m_searchTranslated.setSelected(true);
break;
case UNTRANSLATED:
form.m_searchUntranslated.setSelected(true);
break;
}
// case sensitivity
form.m_replaceCase
.setSelected(Preferences.isPreferenceDefault(Preferences.SEARCHWINDOW_CASE_SENSITIVE_REPLACE, false));
// nbsp as space
form.m_replaceSpaceMatchNbsp.setSelected(Preferences.isPreferenceDefault(
Preferences.SEARCHWINDOW_SPACE_MATCH_NBSP_REPLACE, false));
// replace type
SearchExpression.SearchExpressionType replaceType = SearchExpression.SearchExpressionType
.valueOf(Preferences.getPreferenceEnumDefault(Preferences.SEARCHWINDOW_REPLACE_TYPE,
SearchExpression.SearchExpressionType.EXACT).name());
switch (replaceType) {
case EXACT:
default:
form.m_replaceExactSearchRB.setSelected(true);
break;
case REGEXP:
form.m_replaceRegexpSearchRB.setSelected(true);
break;
}
form.m_replaceUntranslated.setSelected(Preferences.isPreferenceDefault(
Preferences.SEARCHWINDOW_REPLACE_UNTRANSLATED, true));
form.m_searchNotesCB.setSelected(Preferences.isPreferenceDefault(Preferences.SEARCHWINDOW_SEARCH_NOTES, true));
form.m_searchCommentsCB
.setSelected(Preferences.isPreferenceDefault(Preferences.SEARCHWINDOW_SEARCH_COMMENTS, true));
form.m_cbSearchInGlossaries.setSelected(Preferences.isPreferenceDefault(
Preferences.SEARCHWINDOW_GLOSSARY_SEARCH, true));
form.m_cbSearchInMemory.setSelected(Preferences.isPreferenceDefault(Preferences.SEARCHWINDOW_MEMORY_SEARCH,
true));
form.m_cbSearchInTMs.setSelected(Preferences.isPreferenceDefault(Preferences.SEARCHWINDOW_TM_SEARCH, true));
// all results
form.m_allResultsCB.setSelected(Preferences.isPreferenceDefault(Preferences.SEARCHWINDOW_ALL_RESULTS, false));
form.m_fileNamesCB.setSelected(Preferences.isPreferenceDefault(Preferences.SEARCHWINDOW_FILE_NAMES, false));
// editor related options
form.m_autoSyncWithEditor
.setSelected(Preferences.isPreferenceDefault(Preferences.SEARCHWINDOW_AUTO_SYNC, false));
form.m_backToInitialSegment
.setSelected(Preferences.isPreferenceDefault(Preferences.SEARCHWINDOW_BACK_TO_INITIAL_SEGMENT, false));
// update the enabled/selected status of normal options
updateOptionStatus();
// load advanced options settings from user preferences
loadAdvancedOptionPreferences();
// update advanced options status
updateAdvancedOptionStatus();
}
/**
* Saves the size and position of the search window and the button selection
* state
*/
private void savePreferences() {
// search type
if (form.m_searchExactSearchRB.isSelected()) {
Preferences.setPreference(Preferences.SEARCHWINDOW_SEARCH_TYPE,
SearchExpression.SearchExpressionType.EXACT);
} else if (form.m_searchKeywordSearchRB.isSelected()) {
Preferences.setPreference(Preferences.SEARCHWINDOW_SEARCH_TYPE,
SearchExpression.SearchExpressionType.KEYWORD);
} else if (form.m_searchRegexpSearchRB.isSelected()) {
Preferences.setPreference(Preferences.SEARCHWINDOW_SEARCH_TYPE,
SearchExpression.SearchExpressionType.REGEXP);
}
// search options
Preferences.setPreference(Preferences.SEARCHWINDOW_CASE_SENSITIVE, form.m_searchCase.isSelected());
Preferences.setPreference(Preferences.SEARCHWINDOW_SPACE_MATCH_NBSP, form.m_searchSpaceMatchNbsp.isSelected());
Preferences.setPreference(Preferences.SEARCHWINDOW_SEARCH_SOURCE, form.m_searchSource.isSelected());
Preferences.setPreference(Preferences.SEARCHWINDOW_SEARCH_TRANSLATION, form.m_searchTranslation.isSelected());
if (form.m_searchTranslatedUntranslated.isSelected()) {
Preferences.setPreference(Preferences.SEARCHWINDOW_SEARCH_STATE,
SearchExpression.SearchState.TRANSLATED_UNTRANSLATED);
} else if (form.m_searchTranslated.isSelected()) {
Preferences.setPreference(Preferences.SEARCHWINDOW_SEARCH_STATE, SearchExpression.SearchState.TRANSLATED);
} else if (form.m_searchUntranslated.isSelected()) {
Preferences.setPreference(Preferences.SEARCHWINDOW_SEARCH_STATE, SearchExpression.SearchState.UNTRANSLATED);
}
// replace options
Preferences.setPreference(Preferences.SEARCHWINDOW_CASE_SENSITIVE_REPLACE,
form.m_replaceCase.isSelected());
Preferences.setPreference(Preferences.SEARCHWINDOW_SPACE_MATCH_NBSP_REPLACE,
form.m_replaceSpaceMatchNbsp.isSelected());
if (form.m_replaceExactSearchRB.isSelected()) {
Preferences.setPreference(Preferences.SEARCHWINDOW_REPLACE_TYPE,
SearchExpression.SearchExpressionType.EXACT);
} else if (form.m_replaceRegexpSearchRB.isSelected()) {
Preferences.setPreference(Preferences.SEARCHWINDOW_REPLACE_TYPE,
SearchExpression.SearchExpressionType.REGEXP);
}
Preferences.setPreference(Preferences.SEARCHWINDOW_REPLACE_UNTRANSLATED,
form.m_replaceUntranslated.isSelected());
Preferences.setPreference(Preferences.SEARCHWINDOW_SEARCH_NOTES, form.m_searchNotesCB.isSelected());
Preferences.setPreference(Preferences.SEARCHWINDOW_SEARCH_COMMENTS, form.m_searchCommentsCB.isSelected());
Preferences.setPreference(Preferences.SEARCHWINDOW_GLOSSARY_SEARCH, form.m_cbSearchInGlossaries.isSelected());
Preferences.setPreference(Preferences.SEARCHWINDOW_MEMORY_SEARCH, form.m_cbSearchInMemory.isSelected());
Preferences.setPreference(Preferences.SEARCHWINDOW_TM_SEARCH, form.m_cbSearchInTMs.isSelected());
Preferences.setPreference(Preferences.SEARCHWINDOW_ALL_RESULTS, form.m_allResultsCB.isSelected());
Preferences.setPreference(Preferences.SEARCHWINDOW_FILE_NAMES, form.m_fileNamesCB.isSelected());
// advanced search options
Preferences.setPreference(Preferences.SEARCHWINDOW_ADVANCED_VISIBLE, form.m_advancedVisiblePane.isVisible());
Preferences.setPreference(Preferences.SEARCHWINDOW_SEARCH_AUTHOR, form.m_authorCB.isSelected());
Preferences.setPreference(Preferences.SEARCHWINDOW_AUTHOR_NAME, form.m_authorField.getText());
Preferences.setPreference(Preferences.SEARCHWINDOW_DATE_FROM, form.m_dateFromCB.isSelected());
Preferences.setPreference(Preferences.SEARCHWINDOW_DATE_FROM_VALUE,
dateFormat.format(dateFromModel.getDate()));
Preferences.setPreference(Preferences.SEARCHWINDOW_DATE_TO, form.m_dateToCB.isSelected());
Preferences.setPreference(Preferences.SEARCHWINDOW_DATE_TO_VALUE, dateFormat.format(dateToModel.getDate()));
Preferences.setPreference(Preferences.SEARCHWINDOW_NUMBER_OF_RESULTS,
((Integer) form.m_numberOfResults.getValue()));
Preferences.setPreference(Preferences.SEARCHWINDOW_EXCLUDE_ORPHANS, form.m_excludeOrphans.isSelected());
Preferences.setPreference(Preferences.SEARCHWINDOW_FULLHALFWIDTH_INSENSITIVE,
form.m_fullHalfWidthInsensitive.isSelected());
// search dir options
Preferences.setPreference(Preferences.SEARCHWINDOW_DIR, form.m_dirField.getText());
Preferences.setPreference(Preferences.SEARCHWINDOW_SEARCH_FILES, form.m_rbDir.isSelected());
Preferences.setPreference(Preferences.SEARCHWINDOW_RECURSIVE, form.m_recursiveCB.isSelected());
// editor related options
Preferences.setPreference(Preferences.SEARCHWINDOW_AUTO_SYNC, form.m_autoSyncWithEditor.isSelected());
Preferences.setPreference(Preferences.SEARCHWINDOW_BACK_TO_INITIAL_SEGMENT,
form.m_backToInitialSegment.isSelected());
// Search/replace history
HistoryManager.save();
// need to explicitly save preferences
// because project might not be open
Preferences.save();
}
/**
* Updates the enabled/selected status of the options in the dialog.
*/
private void updateOptionStatus() {
setEnabled(form.m_SearchInProjectPane, form.m_rbProject.isSelected());
form.m_rbProject.setEnabled(true);
setEnabled(form.m_SearchInDirPane, form.m_rbDir.isSelected());
form.m_rbDir.setEnabled(true);
}
// //////////////////////////////////////////////////////////////
// interface for displaying text in viewer
/**
* Show search result for user
*/
public void displaySearchResult(final Searcher searcher) {
UIThreadsUtil.executeInSwingThread(new Runnable() {
@Override
public void run() {
EntryListPane viewer = (EntryListPane) form.m_viewer;
viewer.displaySearchResult(searcher, ((Integer) form.m_numberOfResults.getValue()));
form.m_resultsLabel.setText(StringUtil.format(OStrings.getString("SW_NR_OF_RESULTS"),
viewer.getNrEntries()));
boolean haveResults = !searcher.getSearchResults().isEmpty();
form.m_filterButton.setEnabled(haveResults);
form.m_replaceButton.setEnabled(haveResults);
form.m_replaceAllButton.setEnabled(haveResults);
if (!haveResults) {
// RFE#1143 https://sourceforge.net/p/omegat/feature-requests/1143/
form.m_searchField.requestFocus();
form.m_searchField.getEditor().selectAll();
} else {
viewer.requestFocus();
}
}
});
}
// /////////////////////////////////////////////////////////////
// internal functions
private void doBrowseDirectory() {
OmegaTFileChooser browser = new OmegaTFileChooser();
// String str = OStrings.getString("BUTTON_SELECT");
// browser.setApproveButtonText(str);
browser.setDialogTitle(OStrings.getString("SW_TITLE"));
browser.setFileSelectionMode(OmegaTFileChooser.DIRECTORIES_ONLY);
String curDir = form.m_dirField.getText();
if (!curDir.equals("")) {
File dir = new File(curDir);
if (dir.exists() && dir.isDirectory()) {
browser.setCurrentDirectory(dir);
}
}
browser.showOpenDialog(form);
File dir = browser.getSelectedFile();
if (dir == null) {
return;
}
String str = dir.getAbsolutePath() + File.separator;
form.m_dirField.setText(str);
}
private void doFilter() {
EntryListPane viewer = (EntryListPane) form.m_viewer;
Core.getEditor().commitAndLeave(); // Otherwise, the current segment being edited is lost
Core.getEditor().setFilter(new SearchFilter(viewer.getEntryList()));
}
private void doReplace() {
String replaceString = form.m_replaceField.getEditor().getItem().toString();
replaceString = StringUtil.normalizeUnicode(replaceString);
HistoryManager.addReplaceItem(replaceString);
form.m_replaceField.setModel(new DefaultComboBoxModel<>(HistoryManager.getReplaceItems()));
EntryListPane viewer = (EntryListPane) form.m_viewer;
Core.getEditor().commitAndLeave(); // Otherwise, the current segment being edited is lost
Core.getEditor()
.setFilter(
new ReplaceFilter(viewer.getEntryList(), viewer.getSearcher(), replaceString));
}
private void doReplaceAll() {
String replaceString = form.m_replaceField.getEditor().getItem().toString();
replaceString = StringUtil.normalizeUnicode(replaceString);
HistoryManager.addReplaceItem(replaceString);
form.m_replaceField.setModel(new DefaultComboBoxModel<>(HistoryManager.getReplaceItems()));
EntryListPane viewer = (EntryListPane) form.m_viewer;
Core.getEditor().commitAndDeactivate(); // Otherwise, the current segment being edited is lost
int count = viewer.getEntryList().size();
String msg = MessageFormat.format(OStrings.getString("SW_REPLACE_ALL_CONFIRM"), count);
int r = JOptionPane.showConfirmDialog(form, msg, OStrings.getString("CONFIRM_DIALOG_TITLE"),
JOptionPane.YES_NO_OPTION);
if (r == JOptionPane.YES_OPTION) {
new ReplaceFilter(viewer.getEntryList(), viewer.getSearcher(), replaceString).replaceAll();
}
Core.getEditor().activateEntry();
form.m_replaceButton.setEnabled(false);
form.m_replaceAllButton.setEnabled(false);
}
private void doSearch() {
UIThreadsUtil.mustBeSwingThread();
if (thread != null) {
// stop old search thread
thread.fin();
}
EntryListPane viewer = (EntryListPane) form.m_viewer;
String queryString = form.m_searchField.getEditor().getItem().toString();
queryString = StringUtil.normalizeUnicode(queryString);
HistoryManager.addSearchItem(queryString);
form.m_searchField.setModel(new DefaultComboBoxModel<>(HistoryManager.getSearchItems()));
form.m_searchField.requestFocus();
viewer.reset();
String root = null;
if (form.m_rbDir.isSelected()) {
// make sure it's a valid directory name
root = form.m_dirField.getText();
if (!root.endsWith(File.separator)) {
root += File.separator;
}
File f = new File(root);
if (!f.exists() || !f.isDirectory()) {
String error = StringUtil.format(OStrings.getString("SW_ERROR_BAD_DIR"),
form.m_dirField.getText());
form.m_viewer.setText(error);
Log.log(error);
return;
}
// if (m_dirCB.isSelected()) {
// Preferences.setPreference(Preferences.SEARCHWINDOW_DIR, root);
// // need to explicitly save preferences because project
// // might not be open
// Preferences.save();
// }
}
// save user preferences
savePreferences();
if (StringUtil.isEmpty(queryString)) {
form.setTitle(OStrings.getString("SW_TITLE"));
} else {
form.setTitle(queryString + " - OmegaT");
}
SearchExpression s = new SearchExpression();
s.mode = mode;
s.text = queryString;
s.rootDir = root;
s.recursive = form.m_recursiveCB.isSelected();
switch (mode) {
case SEARCH:
if (form.m_searchExactSearchRB.isSelected()) {
s.searchExpressionType = SearchExpression.SearchExpressionType.EXACT;
} else if (form.m_searchKeywordSearchRB.isSelected()) {
s.searchExpressionType = SearchExpression.SearchExpressionType.KEYWORD;
} else if (form.m_searchRegexpSearchRB.isSelected()) {
s.searchExpressionType = SearchExpression.SearchExpressionType.REGEXP;
}
s.caseSensitive = form.m_searchCase.isSelected();
s.spaceMatchNbsp = form.m_searchSpaceMatchNbsp.isSelected();
s.glossary = mode == SearchMode.SEARCH ? form.m_cbSearchInGlossaries.isSelected() : false;
s.memory = mode == SearchMode.SEARCH ? form.m_cbSearchInMemory.isSelected() : true;
s.tm = mode == SearchMode.SEARCH ? form.m_cbSearchInTMs.isSelected() : false;
s.allResults = mode == SearchMode.SEARCH ? form.m_allResultsCB.isSelected() : true;
s.fileNames = mode == SearchMode.SEARCH ? form.m_fileNamesCB.isSelected() : true;
s.searchSource = form.m_searchSource.isSelected();
s.searchTarget = form.m_searchTranslation.isSelected();
if (form.m_searchTranslatedUntranslated.isSelected()) {
s.searchTranslated = true;
s.searchUntranslated = true;
} else if (form.m_searchTranslated.isSelected()) {
s.searchTranslated = true;
s.searchUntranslated = false;
} else if (form.m_searchUntranslated.isSelected()) {
s.searchTranslated = false;
s.searchUntranslated = true;
}
s.widthInsensitive = form.m_fullHalfWidthInsensitive.isSelected();
s.excludeOrphans = form.m_excludeOrphans.isSelected();
break;
case REPLACE:
if (form.m_replaceExactSearchRB.isSelected()) {
s.searchExpressionType = SearchExpression.SearchExpressionType.EXACT;
} else if (form.m_replaceRegexpSearchRB.isSelected()) {
s.searchExpressionType = SearchExpression.SearchExpressionType.REGEXP;
}
s.caseSensitive = form.m_replaceCase.isSelected();
s.spaceMatchNbsp = form.m_replaceSpaceMatchNbsp.isSelected();
s.glossary = false;
s.memory = true;
s.tm = false;
s.allResults = true;
s.fileNames = Core.getProject().getProjectFiles().size() > 1;
s.searchSource = false;
s.searchTarget = false;
s.searchTranslated = false;
s.searchUntranslated = false;
s.replaceTranslated = true;
s.replaceUntranslated = form.m_replaceUntranslated.isSelected();
s.widthInsensitive = form.m_fullHalfWidthInsensitive.isSelected();
s.excludeOrphans = true;
break;
}
s.searchNotes = form.m_searchNotesCB.isSelected();
s.searchComments = form.m_searchCommentsCB.isSelected();
s.searchAuthor = form.m_authorCB.isSelected();
s.author = form.m_authorField.getText();
s.searchDateAfter = form.m_dateFromCB.isSelected();
s.dateAfter = dateFromModel.getDate().getTime();
s.searchDateBefore = form.m_dateToCB.isSelected();
s.dateBefore = dateToModel.getDate().getTime();
s.numberOfResults = mode == SearchMode.SEARCH ? ((Integer) form.m_numberOfResults.getValue())
: Integer.MAX_VALUE;
Searcher searcher = new Searcher(Core.getProject(), s);
// start the search in a separate thread
thread = new SearchThread(this, searcher);
thread.start();
}
void doCancel() {
UIThreadsUtil.mustBeSwingThread();
if (thread != null) {
thread.fin();
}
form.dispose();
}
public void dispose() {
form.dispose();
}
/**
* Make Search window visible on screen, with optional initial query (may be
* null).
*
* @param query
* Initial query string (may be empty or null)
*/
public void makeVisible(String query) {
if (!StringUtil.isEmpty(query)) {
((JTextField) form.m_searchField.getEditor().getEditorComponent()).setText(query);
}
form.setVisible(true);
form.setState(JFrame.NORMAL);
form.m_searchField.requestFocus();
}
private boolean isSegmentDisplayed(int entry) {
IEditorFilter filter = Core.getEditor().getFilter();
if (filter == null) {
return true;
} else {
SourceTextEntry ste = Core.getProject().getAllEntries().get(entry - 1);
return filter.allowed(ste);
}
}
private void setAdvancedOptionsVisible(boolean visible) {
form.m_advancedVisiblePane.setVisible(visible);
Mnemonics.setLocalizedText(form.m_advancedButton,
visible ? OStrings.getString("SW_HIDE_ADVANCED_OPTIONS")
: OStrings.getString("SW_SHOW_ADVANCED_OPTIONS"));
updateAdvancedOptionStatus();
}
private void enableDisableAuthor() {
boolean editable = form.m_authorCB.isSelected();
form.m_authorField.setEditable(editable);
if (editable) {
// move focus to author field
form.m_authorField.requestFocus();
} else {
// move focus to search edit field
form.m_searchField.requestFocus();
}
}
private void enableDisableDateFrom() {
boolean enable = form.m_dateFromCB.isSelected();
form.m_dateFromSpinner.setEnabled(enable);
form.m_dateFromButton.setEnabled(enable);
if (enable) {
// move focus to date spinner
form.m_dateFromSpinner.requestFocus();
} else {
// move focus to search edit field
form.m_searchField.requestFocus();
}
}
private void doResetDateFrom() {
Calendar calendar = Calendar.getInstance();
Date now = calendar.getTime();
dateFromModel.setEnd(now);
dateFromModel.setValue(now);
}
private void doResetDateTo() {
Calendar calendar = Calendar.getInstance();
Date now = calendar.getTime();
dateToModel.setEnd(now);
dateToModel.setValue(now);
}
private void enableDisableDateTo() {
boolean enable = form.m_dateToCB.isSelected();
form.m_dateToSpinner.setEnabled(enable);
form.m_dateToButton.setEnabled(enable);
if (enable) {
// move focus to date spinner
form.m_dateToSpinner.requestFocus();
} else {
// move focus to search edit field
form.m_searchField.requestFocus();
}
}
private void loadAdvancedOptionPreferences() {
// advanced options visibility
setAdvancedOptionsVisible(Preferences.isPreference(Preferences.SEARCHWINDOW_ADVANCED_VISIBLE));
// author options
form.m_authorCB.setSelected(Preferences.isPreference(Preferences.SEARCHWINDOW_SEARCH_AUTHOR));
form.m_authorField.setText(Preferences.getPreference(Preferences.SEARCHWINDOW_AUTHOR_NAME));
// date options
try {
// from date
form.m_dateFromCB.setSelected(Preferences.isPreference(Preferences.SEARCHWINDOW_DATE_FROM));
String dateFromValue = Preferences.getPreference(Preferences.SEARCHWINDOW_DATE_FROM_VALUE);
if (!StringUtil.isEmpty(dateFromValue)) {
dateFromModel.setValue(dateFormat.parse(dateFromValue));
}
// to date
form.m_dateToCB.setSelected(Preferences.isPreference(Preferences.SEARCHWINDOW_DATE_TO));
String dateToValue = Preferences.getPreference(Preferences.SEARCHWINDOW_DATE_TO_VALUE);
if (!StringUtil.isEmpty(dateToValue)) {
dateToModel.setValue(dateFormat.parse(dateToValue));
}
} catch (ParseException e) {
// use safe settings in case of parsing error
form.m_dateFromCB.setSelected(false);
form.m_dateToCB.setSelected(false);
}
// Number of results
form.m_numberOfResults.setValue(Preferences.getPreferenceDefault(Preferences.SEARCHWINDOW_NUMBER_OF_RESULTS,
OConsts.ST_MAX_SEARCH_RESULTS));
form.m_excludeOrphans.setSelected(Preferences.isPreference(Preferences.SEARCHWINDOW_EXCLUDE_ORPHANS));
form.m_fullHalfWidthInsensitive
.setSelected(Preferences.isPreference(Preferences.SEARCHWINDOW_FULLHALFWIDTH_INSENSITIVE));
// if advanced options are enabled (e.g. author/date search),
// let the user see them anyway. This is important because
// search results will be affected by these settings
if (form.m_authorCB.isSelected() || form.m_dateFromCB.isSelected() || form.m_dateToCB.isSelected()
|| form.m_excludeOrphans.isSelected() || form.m_fullHalfWidthInsensitive.isSelected()) {
setAdvancedOptionsVisible(true);
}
}
private void updateAdvancedOptionStatus() {
form.m_authorField.setEditable(form.m_authorCB.isSelected());
form.m_dateFromSpinner.setEnabled(form.m_dateFromCB.isSelected());
form.m_dateFromButton.setEnabled(form.m_dateFromCB.isSelected());
form.m_dateToSpinner.setEnabled(form.m_dateToCB.isSelected());
form.m_dateToButton.setEnabled(form.m_dateToCB.isSelected());
}
/**
* Set enabled/disabled component and all his children.
*
* @param component
* @param enabled
*/
private void setEnabled(Container component, boolean enabled) {
component.setEnabled(enabled);
for (int i = 0; i < component.getComponentCount(); i++) {
Component c = component.getComponent(i);
c.setEnabled(enabled);
if (c instanceof Container) {
setEnabled((Container) c, enabled);
}
}
}
private CaretPosition getCurrentPositionInEntryTranslationInEditor(IEditor editor) {
if (editor instanceof EditorController) {
EditorController c = (EditorController) editor;
int selectionEnd = c.getCurrentPositionInEntryTranslation();
String selection = c.getSelectedText();
String translation = c.getCurrentTranslation();
if (StringUtil.isEmpty(translation) || StringUtil.isEmpty(selection)) {
// no translation or no selection
return new CaretPosition(selectionEnd);
} else {
// get selected range
int selectionStart = selectionEnd;
int pos = 0;
do {
pos = translation.indexOf(selection, pos);
if (pos == selectionEnd) {
selectionStart = pos;
selectionEnd = pos + selection.length();
break;
} else if ((pos + selection.length()) == selectionEnd) {
selectionStart = pos;
break;
}
pos++;
} while (pos > 0);
return new CaretPosition(selectionStart, selectionEnd);
}
} else {
return CaretPosition.startOfEntry();
}
}
public void addWindowListener(WindowListener listener) {
form.addWindowListener(listener);
}
/**
* Display message dialog with the error as message
*
* @param ex
* exception to show
* @param errorKey
* error message key in resource bundle
* @param params
* error text parameters
*/
public void displayErrorRB(final Throwable ex, final String errorKey, final Object... params) {
UIThreadsUtil.executeInSwingThread(new Runnable() {
@Override
public void run() {
String msg;
if (params != null) {
msg = StringUtil.format(OStrings.getString(errorKey), params);
} else {
msg = OStrings.getString(errorKey);
}
String fulltext = msg;
if (ex != null) {
fulltext += "\n" + ex.getLocalizedMessage();
}
JOptionPane.showMessageDialog(form, fulltext, OStrings.getString("TF_ERROR"),
JOptionPane.ERROR_MESSAGE);
}
});
}
private SimpleDateFormat dateFormat;
private SpinnerDateModel dateFromModel, dateToModel;
private SearchThread thread;
private static final String SAVED_DATE_FORMAT = "yyyy/MM/dd HH:mm";
}