search dialog redesign
This commit is contained in:
parent
461bb74503
commit
9b93d34988
@ -11,6 +11,7 @@ import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.zip.*;
|
||||
|
||||
import net.lingala.zip4j.ZipFile;
|
||||
@ -656,35 +657,54 @@ public class FileOperations {
|
||||
* Search files by filename pattern and/or content text.
|
||||
* If both are provided, both must match.
|
||||
*/
|
||||
public static void search(File directory, String filenamePattern, String contentText, boolean recursive, boolean searchArchives, boolean wholeWord, boolean caseSensitive, SearchCallback callback) throws IOException {
|
||||
public static void search(File directory, String filenamePattern, String contentText, int maxDepth, boolean searchArchives, boolean wholeWord, boolean caseSensitive, boolean filenameIsRegex, boolean contentIsRegex, SearchCallback callback) throws IOException {
|
||||
Pattern filenameRegex = null;
|
||||
String filenameLower = null;
|
||||
if (filenamePattern != null && !filenamePattern.isEmpty()) {
|
||||
filenameLower = filenamePattern.toLowerCase();
|
||||
if (filenamePattern.contains("*") || filenamePattern.contains("?")) {
|
||||
String regex = filenamePattern
|
||||
.replace(".", "\\.")
|
||||
.replace("*", ".*")
|
||||
.replace("?", ".");
|
||||
filenameRegex = Pattern.compile(regex, Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE);
|
||||
if (filenameIsRegex) {
|
||||
try {
|
||||
filenameRegex = Pattern.compile(filenamePattern, caseSensitive ? 0 : Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE);
|
||||
} catch (Exception e) {
|
||||
throw new IOException("Invalid filename RegEx: " + e.getMessage());
|
||||
}
|
||||
} else {
|
||||
filenameLower = caseSensitive ? filenamePattern : filenamePattern.toLowerCase();
|
||||
if (filenamePattern.contains("*") || filenamePattern.contains("?")) {
|
||||
String regex = filenamePattern
|
||||
.replace(".", "\\.")
|
||||
.replace("*", ".*")
|
||||
.replace("?", ".");
|
||||
filenameRegex = Pattern.compile(regex, caseSensitive ? 0 : Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Pattern contentPattern = null;
|
||||
if (contentText != null && !contentText.isEmpty()) {
|
||||
String quote = Pattern.quote(contentText);
|
||||
String regex = wholeWord ? "(?<!\\w)" + quote + "(?!\\w)" : quote;
|
||||
int flags = Pattern.UNICODE_CASE;
|
||||
if (!caseSensitive) {
|
||||
flags |= Pattern.CASE_INSENSITIVE;
|
||||
}
|
||||
contentPattern = Pattern.compile(regex, flags);
|
||||
|
||||
String regex;
|
||||
if (contentIsRegex) {
|
||||
regex = contentText;
|
||||
} else {
|
||||
String quote = Pattern.quote(contentText);
|
||||
regex = wholeWord ? "(?<!\\w)" + quote + "(?!\\w)" : quote;
|
||||
}
|
||||
|
||||
try {
|
||||
contentPattern = Pattern.compile(regex, flags);
|
||||
} catch (Exception e) {
|
||||
throw new IOException("Invalid content RegEx: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
searchRecursive(directory.toPath(), filenameLower, filenameRegex, contentPattern, recursive, searchArchives, callback);
|
||||
searchRecursive(directory.toPath(), filenameLower, filenameRegex, contentPattern, maxDepth, 0, searchArchives, caseSensitive, callback);
|
||||
}
|
||||
|
||||
private static void searchRecursive(Path directory, String patternLower, Pattern filenameRegex, Pattern contentPattern, boolean recursive, boolean searchArchives, SearchCallback callback) throws IOException {
|
||||
private static void searchRecursive(Path directory, String patternLower, Pattern filenameRegex, Pattern contentPattern, int maxDepth, int currentDepth, boolean searchArchives, boolean caseSensitive, SearchCallback callback) throws IOException {
|
||||
if (callback != null && callback.isCancelled()) return;
|
||||
|
||||
// Use absolute path for reliable prefix checking
|
||||
@ -708,14 +728,14 @@ public class FileOperations {
|
||||
}
|
||||
|
||||
if (Files.isDirectory(entry)) {
|
||||
if (recursive) {
|
||||
searchRecursive(entry, patternLower, filenameRegex, contentPattern, recursive, searchArchives, callback);
|
||||
if (maxDepth == -1 || currentDepth < maxDepth) {
|
||||
searchRecursive(entry, patternLower, filenameRegex, contentPattern, maxDepth, currentDepth + 1, searchArchives, caseSensitive, callback);
|
||||
}
|
||||
} else {
|
||||
File file = entry.toFile();
|
||||
boolean nameMatched = true;
|
||||
if (patternLower != null && !patternLower.isEmpty()) {
|
||||
nameMatched = matchName(file.getName(), patternLower, filenameRegex);
|
||||
nameMatched = matchName(file.getName(), patternLower, filenameRegex, caseSensitive);
|
||||
}
|
||||
|
||||
boolean contentMatched = true;
|
||||
@ -729,7 +749,7 @@ public class FileOperations {
|
||||
|
||||
// SEARCH IN ARCHIVES
|
||||
if (searchArchives && isArchiveFile(file)) {
|
||||
searchInArchiveCombined(file, patternLower, filenameRegex, contentPattern, callback);
|
||||
searchInArchiveCombined(file, patternLower, filenameRegex, contentPattern, caseSensitive, callback);
|
||||
}
|
||||
}
|
||||
} catch (AccessDeniedException e) {
|
||||
@ -741,13 +761,15 @@ public class FileOperations {
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean matchName(String name, String patternLower, Pattern filenameRegex) {
|
||||
String nameLower = name.toLowerCase();
|
||||
if (nameLower.contains(patternLower)) return true;
|
||||
private static boolean matchName(String name, String patternLower, Pattern filenameRegex, boolean caseSensitive) {
|
||||
if (filenameRegex != null) {
|
||||
return filenameRegex.matcher(name).matches();
|
||||
}
|
||||
return false;
|
||||
if (caseSensitive) {
|
||||
return name.contains(patternLower);
|
||||
} else {
|
||||
return name.toLowerCase().contains(patternLower.toLowerCase());
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean fileMatchesContent(Path entry, Pattern contentPattern) {
|
||||
@ -847,7 +869,7 @@ public class FileOperations {
|
||||
return n.endsWith(".war") || n.endsWith(".zip") || n.endsWith(".jar") || n.endsWith(".tar") || n.endsWith(".tar.gz") || n.endsWith(".tgz") || n.endsWith(".7z") || n.endsWith(".rar");
|
||||
}
|
||||
|
||||
private static void searchInArchiveCombined(File archive, String patternLower, Pattern filenameRegex, Pattern contentPattern, SearchCallback callback) {
|
||||
private static void searchInArchiveCombined(File archive, String patternLower, Pattern filenameRegex, Pattern contentPattern, boolean caseSensitive, SearchCallback callback) {
|
||||
if (callback != null && callback.isCancelled()) return;
|
||||
String name = archive.getName().toLowerCase();
|
||||
try {
|
||||
@ -859,7 +881,7 @@ public class FileOperations {
|
||||
if (!entry.isDirectory()) {
|
||||
boolean nameMatched = true;
|
||||
if (patternLower != null && !patternLower.isEmpty()) {
|
||||
nameMatched = matchEntry(entry.getName(), patternLower, filenameRegex);
|
||||
nameMatched = matchEntry(entry.getName(), patternLower, filenameRegex, caseSensitive);
|
||||
}
|
||||
boolean contentMatched = true;
|
||||
if (nameMatched && contentPattern != null) {
|
||||
@ -883,7 +905,7 @@ public class FileOperations {
|
||||
if (!entry.isDirectory()) {
|
||||
boolean nameMatched = true;
|
||||
if (patternLower != null && !patternLower.isEmpty()) {
|
||||
nameMatched = matchEntry(entry.getName(), patternLower, filenameRegex);
|
||||
nameMatched = matchEntry(entry.getName(), patternLower, filenameRegex, caseSensitive);
|
||||
}
|
||||
boolean contentMatched = true;
|
||||
if (nameMatched && contentPattern != null) {
|
||||
@ -903,7 +925,7 @@ public class FileOperations {
|
||||
if (!entry.isDirectory()) {
|
||||
boolean nameMatched = true;
|
||||
if (patternLower != null && !patternLower.isEmpty()) {
|
||||
nameMatched = matchEntry(entry.getName(), patternLower, filenameRegex);
|
||||
nameMatched = matchEntry(entry.getName(), patternLower, filenameRegex, caseSensitive);
|
||||
}
|
||||
boolean contentMatched = true;
|
||||
if (nameMatched && contentPattern != null) {
|
||||
@ -931,7 +953,7 @@ public class FileOperations {
|
||||
if (!fh.isDirectory()) {
|
||||
boolean nameMatched = true;
|
||||
if (patternLower != null && !patternLower.isEmpty()) {
|
||||
nameMatched = matchEntry(fh.getFileName(), patternLower, filenameRegex);
|
||||
nameMatched = matchEntry(fh.getFileName(), patternLower, filenameRegex, caseSensitive);
|
||||
}
|
||||
boolean contentMatched = true;
|
||||
if (nameMatched && contentPattern != null) {
|
||||
@ -952,16 +974,13 @@ public class FileOperations {
|
||||
}
|
||||
|
||||
|
||||
private static boolean matchEntry(String entryName, String patternLower, Pattern filenameRegex) {
|
||||
private static boolean matchEntry(String entryName, String patternLower, Pattern filenameRegex, boolean caseSensitive) {
|
||||
if (entryName == null) return false;
|
||||
String nameShort = entryName;
|
||||
int lastSlash = entryName.lastIndexOf('/');
|
||||
if (lastSlash != -1) nameShort = entryName.substring(lastSlash + 1);
|
||||
|
||||
String nameLower = nameShort.toLowerCase();
|
||||
if (nameLower.contains(patternLower)) return true;
|
||||
if (filenameRegex != null && filenameRegex.matcher(nameShort).matches()) return true;
|
||||
return false;
|
||||
return matchName(nameShort, patternLower, filenameRegex, caseSensitive);
|
||||
}
|
||||
|
||||
private static boolean searchInStream(InputStream is, Pattern contentPattern) {
|
||||
|
||||
@ -53,19 +53,22 @@ public class FileEditor extends JFrame {
|
||||
private JTextField searchField;
|
||||
private JCheckBox wholeWordCheckBox;
|
||||
private JCheckBox caseSensitiveCheckBox;
|
||||
private JCheckBox regexCheckBox;
|
||||
private JLabel searchStatusLabel;
|
||||
private static String lastSearchValue = "";
|
||||
private static boolean lastWholeWord = false;
|
||||
private static boolean lastCaseSensitive = false;
|
||||
private static boolean lastRegex = false;
|
||||
|
||||
public static void setLastSearchValue(String value) {
|
||||
lastSearchValue = value;
|
||||
}
|
||||
|
||||
public static void setLastSearchOptions(String value, boolean wholeWord, boolean caseSensitive) {
|
||||
public static void setLastSearchOptions(String value, boolean wholeWord, boolean caseSensitive, boolean regex) {
|
||||
lastSearchValue = value;
|
||||
lastWholeWord = wholeWord;
|
||||
lastCaseSensitive = caseSensitive;
|
||||
lastRegex = regex;
|
||||
}
|
||||
|
||||
public FileEditor(Window parent, File file, AppConfig config, boolean readOnly) {
|
||||
@ -118,6 +121,7 @@ public class FileEditor extends JFrame {
|
||||
|
||||
wholeWordCheckBox = new JCheckBox("Whole word");
|
||||
caseSensitiveCheckBox = new JCheckBox("Case sensitive");
|
||||
regexCheckBox = new JCheckBox("RegEx");
|
||||
|
||||
JButton nextBtn = new JButton("Next");
|
||||
nextBtn.addActionListener(e -> findNext());
|
||||
@ -135,6 +139,7 @@ public class FileEditor extends JFrame {
|
||||
searchPanel.add(searchField);
|
||||
searchPanel.add(wholeWordCheckBox);
|
||||
searchPanel.add(caseSensitiveCheckBox);
|
||||
searchPanel.add(regexCheckBox);
|
||||
searchPanel.add(nextBtn);
|
||||
searchPanel.add(prevBtn);
|
||||
searchPanel.add(closeBtn);
|
||||
@ -147,6 +152,7 @@ public class FileEditor extends JFrame {
|
||||
searchPanel.setVisible(true);
|
||||
wholeWordCheckBox.setSelected(lastWholeWord);
|
||||
caseSensitiveCheckBox.setSelected(lastCaseSensitive);
|
||||
regexCheckBox.setSelected(lastRegex);
|
||||
String selection = textArea.getSelectedText();
|
||||
if (selection != null && !selection.isEmpty() && !selection.contains("\n")) {
|
||||
searchField.setText(selection);
|
||||
@ -194,6 +200,7 @@ public class FileEditor extends JFrame {
|
||||
lastSearchValue = text;
|
||||
lastWholeWord = wholeWordCheckBox.isSelected();
|
||||
lastCaseSensitive = caseSensitiveCheckBox.isSelected();
|
||||
lastRegex = regexCheckBox.isSelected();
|
||||
updateSearchHistory(text);
|
||||
|
||||
if (searchField.getText().isEmpty()) searchField.setText(text);
|
||||
@ -205,23 +212,38 @@ public class FileEditor extends JFrame {
|
||||
if (textArea.getSelectionEnd() > textArea.getSelectionStart()) {
|
||||
String selected = content.substring(textArea.getSelectionStart(), textArea.getSelectionEnd());
|
||||
boolean match;
|
||||
if (lastCaseSensitive) {
|
||||
match = selected.equals(text);
|
||||
if (lastRegex) {
|
||||
try {
|
||||
int flags = (!lastCaseSensitive) ? (Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE) : 0;
|
||||
if (Pattern.compile(text, flags).matcher(selected).matches()) {
|
||||
start = textArea.getSelectionEnd();
|
||||
}
|
||||
} catch (Exception ignore) {}
|
||||
} else {
|
||||
match = selected.equalsIgnoreCase(text);
|
||||
}
|
||||
if (match) {
|
||||
start = textArea.getSelectionEnd();
|
||||
if (lastCaseSensitive) {
|
||||
match = selected.equals(text);
|
||||
} else {
|
||||
match = selected.equalsIgnoreCase(text);
|
||||
}
|
||||
if (match) {
|
||||
start = textArea.getSelectionEnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String quote = Pattern.quote(text);
|
||||
String regex = lastWholeWord ? "(?<!\\w)" + quote + "(?!\\w)" : quote;
|
||||
String patternStr = lastRegex ? text : Pattern.quote(text);
|
||||
String regex = (lastWholeWord && !lastRegex) ? "(?<!\\w)" + patternStr + "(?!\\w)" : patternStr;
|
||||
int flags = 0;
|
||||
if (!lastCaseSensitive) {
|
||||
flags |= Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE;
|
||||
}
|
||||
Pattern pattern = Pattern.compile(regex, flags);
|
||||
Pattern pattern;
|
||||
try {
|
||||
pattern = Pattern.compile(regex, flags);
|
||||
} catch (Exception ex) {
|
||||
JOptionPane.showMessageDialog(this, "Invalid Regular Expression: " + ex.getMessage());
|
||||
return;
|
||||
}
|
||||
Matcher matcher = pattern.matcher(content);
|
||||
|
||||
if (matcher.find(start)) {
|
||||
@ -263,6 +285,7 @@ public class FileEditor extends JFrame {
|
||||
lastSearchValue = text;
|
||||
lastWholeWord = wholeWordCheckBox.isSelected();
|
||||
lastCaseSensitive = caseSensitiveCheckBox.isSelected();
|
||||
lastRegex = regexCheckBox.isSelected();
|
||||
updateSearchHistory(text);
|
||||
|
||||
if (searchField.getText().isEmpty()) searchField.setText(text);
|
||||
@ -271,13 +294,19 @@ public class FileEditor extends JFrame {
|
||||
int start = textArea.getSelectionStart();
|
||||
if (start < 0) start = content.length();
|
||||
|
||||
String quote = Pattern.quote(text);
|
||||
String regex = lastWholeWord ? "(?<!\\w)" + quote + "(?!\\w)" : quote;
|
||||
String patternStr = lastRegex ? text : Pattern.quote(text);
|
||||
String regex = (lastWholeWord && !lastRegex) ? "(?<!\\w)" + patternStr + "(?!\\w)" : patternStr;
|
||||
int flags = 0;
|
||||
if (!lastCaseSensitive) {
|
||||
flags |= Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE;
|
||||
}
|
||||
Pattern pattern = Pattern.compile(regex, flags);
|
||||
Pattern pattern;
|
||||
try {
|
||||
pattern = Pattern.compile(regex, flags);
|
||||
} catch (Exception ex) {
|
||||
JOptionPane.showMessageDialog(this, "Invalid Regular Expression: " + ex.getMessage());
|
||||
return;
|
||||
}
|
||||
Matcher matcher = pattern.matcher(content);
|
||||
|
||||
int lastMatchStart = -1;
|
||||
|
||||
@ -8,6 +8,7 @@ import java.awt.event.KeyEvent;
|
||||
import java.awt.event.InputEvent;
|
||||
import javax.swing.table.AbstractTableModel;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.io.File;
|
||||
import java.awt.Desktop;
|
||||
import java.util.ArrayList;
|
||||
@ -19,12 +20,17 @@ import java.util.List;
|
||||
public class SearchDialog extends JDialog {
|
||||
|
||||
private JComboBox<String> patternCombo;
|
||||
private JComboBox<String> searchInCombo;
|
||||
private JComboBox<String> contentPatternCombo;
|
||||
private JComboBox<String> subDirCombo;
|
||||
private JCheckBox recursiveCheckBox;
|
||||
private JCheckBox contentSearchCheckBox;
|
||||
private JCheckBox archiveSearchCheckBox;
|
||||
private JCheckBox wholeWordCheckBox;
|
||||
private JCheckBox caseSensitiveCheckBox;
|
||||
private JCheckBox regexCheckBox;
|
||||
private JCheckBox regexContentCheckBox;
|
||||
private JCheckBox everythingCheckBox;
|
||||
private JTable resultsTable;
|
||||
private ResultsTableModel tableModel;
|
||||
private JButton searchButton;
|
||||
@ -69,19 +75,28 @@ public class SearchDialog extends JDialog {
|
||||
Color bg = config.getBackgroundColor();
|
||||
if (bg == null) return;
|
||||
updateComponentBackground(getContentPane(), bg);
|
||||
// Ensure some panels are transparent to let the main background show through
|
||||
if (resultsTable.getTableHeader() != null) {
|
||||
resultsTable.getTableHeader().setBackground(bg);
|
||||
resultsTable.getTableHeader().setForeground(isDark(bg) ? Color.WHITE : Color.BLACK);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateComponentBackground(Container container, Color bg) {
|
||||
if (container == null) return;
|
||||
container.setBackground(bg);
|
||||
if (!(container instanceof JViewport)) {
|
||||
container.setBackground(bg);
|
||||
}
|
||||
boolean dark = isDark(bg);
|
||||
Color selColor = config != null ? config.getSelectionColor() : null;
|
||||
|
||||
for (Component c : container.getComponents()) {
|
||||
if (c instanceof JPanel || c instanceof JToolBar || c instanceof JScrollPane || c instanceof JViewport ||
|
||||
if (c instanceof JPanel || c instanceof JToolBar || c instanceof JScrollPane ||
|
||||
c instanceof JTabbedPane || c instanceof JButton || c instanceof JSplitPane ||
|
||||
c instanceof JList || c instanceof JComboBox || c instanceof JTable) {
|
||||
c.setBackground(bg);
|
||||
if (!(c instanceof JViewport)) {
|
||||
c.setBackground(bg);
|
||||
}
|
||||
if (c instanceof JTable t) {
|
||||
if (t.getTableHeader() != null) {
|
||||
t.getTableHeader().setBackground(bg);
|
||||
@ -128,98 +143,145 @@ public class SearchDialog extends JDialog {
|
||||
setLayout(new BorderLayout(10, 10));
|
||||
((JComponent) getContentPane()).setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
|
||||
|
||||
// Panel pro zadání kritérií
|
||||
JPanel searchPanel = new JPanel(new GridBagLayout());
|
||||
// Panel pro zadání kritérií (NORTH panel)
|
||||
JPanel northPanel = new JPanel(new GridBagLayout());
|
||||
GridBagConstraints gbc = new GridBagConstraints();
|
||||
gbc.insets = new Insets(5, 5, 5, 5);
|
||||
gbc.insets = new Insets(2, 5, 2, 5);
|
||||
gbc.fill = GridBagConstraints.HORIZONTAL;
|
||||
gbc.anchor = GridBagConstraints.WEST;
|
||||
|
||||
// Row 0: Search for: [patternCombo]
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 0;
|
||||
searchPanel.add(new JLabel("Search for:"), gbc);
|
||||
gbc.weightx = 0;
|
||||
northPanel.add(new JLabel("Search for:"), gbc);
|
||||
|
||||
gbc.gridx = 1;
|
||||
gbc.gridwidth = 2;
|
||||
gbc.weightx = 1.0;
|
||||
java.util.List<String> history = config != null ? config.getSearchHistory() : java.util.Collections.emptyList();
|
||||
patternCombo = new JComboBox<>(new DefaultComboBoxModel<>(history.toArray(new String[0])));
|
||||
patternCombo.setEditable(true);
|
||||
try { patternCombo.setSelectedItem(""); patternCombo.getEditor().setItem(""); } catch (Exception ignore) {}
|
||||
northPanel.add(patternCombo, gbc);
|
||||
|
||||
// Row 1: Search in: [searchInCombo] [>>] [Drives]
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 1;
|
||||
gbc.gridwidth = 1;
|
||||
gbc.weightx = 0;
|
||||
northPanel.add(new JLabel("Search in:"), gbc);
|
||||
|
||||
gbc.gridx = 1;
|
||||
gbc.weightx = 1.0;
|
||||
// Pattern input is an editable combo box populated from history
|
||||
java.util.List<String> history = config != null ? config.getSearchHistory() : java.util.Collections.emptyList();
|
||||
javax.swing.DefaultComboBoxModel<String> historyModel = new javax.swing.DefaultComboBoxModel<>();
|
||||
for (String h : history) historyModel.addElement(h);
|
||||
patternCombo = new JComboBox<>(historyModel);
|
||||
patternCombo.setEditable(true);
|
||||
// start with an empty editor so the field is blank on open
|
||||
try {
|
||||
patternCombo.setSelectedItem("");
|
||||
patternCombo.getEditor().setItem("");
|
||||
} catch (Exception ignore) {}
|
||||
patternCombo.setToolTipText("Enter filename or pattern (* for any chars, ? for single char)");
|
||||
searchPanel.add(patternCombo, gbc);
|
||||
gbc.gridwidth = 2; // Span across to the end since we removed side buttons
|
||||
searchInCombo = new JComboBox<>(new String[]{searchDirectory.getAbsolutePath()});
|
||||
searchInCombo.setEditable(true);
|
||||
northPanel.add(searchInCombo, gbc);
|
||||
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 1;
|
||||
gbc.gridwidth = 2;
|
||||
recursiveCheckBox = new JCheckBox("Include subdirectories", true);
|
||||
searchPanel.add(recursiveCheckBox, gbc);
|
||||
|
||||
// Row for content text pattern
|
||||
// Row 2: [ ] RegEx [ ] Everything
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 2;
|
||||
gbc.gridwidth = 1;
|
||||
searchPanel.add(new JLabel("Text:"), gbc);
|
||||
|
||||
gbc.weightx = 0;
|
||||
regexCheckBox = new JCheckBox("RegEx");
|
||||
northPanel.add(regexCheckBox, gbc);
|
||||
|
||||
gbc.gridx = 1;
|
||||
gbc.weightx = 1.0;
|
||||
java.util.List<String> contentHistory = config != null ? config.getContentSearchHistory() : java.util.Collections.emptyList();
|
||||
javax.swing.DefaultComboBoxModel<String> contentHistoryModel = new javax.swing.DefaultComboBoxModel<>();
|
||||
for (String h : contentHistory) contentHistoryModel.addElement(h);
|
||||
contentPatternCombo = new JComboBox<>(contentHistoryModel);
|
||||
contentPatternCombo.setEditable(true);
|
||||
try { contentPatternCombo.setSelectedItem(""); contentPatternCombo.getEditor().setItem(""); } catch (Exception ignore) {}
|
||||
contentPatternCombo.setToolTipText("Text to search inside files");
|
||||
searchPanel.add(contentPatternCombo, gbc);
|
||||
gbc.gridwidth = 2;
|
||||
everythingCheckBox = new JCheckBox("'Everything'");
|
||||
northPanel.add(everythingCheckBox, gbc);
|
||||
|
||||
gbc.gridx = 0;
|
||||
// Row 3: [ ] Search archives ...
|
||||
gbc.gridx = 1;
|
||||
gbc.gridy = 3;
|
||||
gbc.gridwidth = 2;
|
||||
contentSearchCheckBox = new JCheckBox("Search inside file contents", false);
|
||||
searchPanel.add(contentSearchCheckBox, gbc);
|
||||
archiveSearchCheckBox = new JCheckBox("Search archives (all except for UC2)", false);
|
||||
northPanel.add(archiveSearchCheckBox, gbc);
|
||||
|
||||
// Row 4: Search in subdirectories: [ComboBox]
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 4;
|
||||
gbc.gridwidth = 1;
|
||||
wholeWordCheckBox = new JCheckBox("Whole word only", false);
|
||||
searchPanel.add(wholeWordCheckBox, gbc);
|
||||
|
||||
northPanel.add(new JLabel("Search in subdirectories:"), gbc);
|
||||
|
||||
gbc.gridx = 1;
|
||||
caseSensitiveCheckBox = new JCheckBox("Case sensitive", false);
|
||||
searchPanel.add(caseSensitiveCheckBox, gbc);
|
||||
|
||||
gbc.gridwidth = 2;
|
||||
String[] subDirOptions = {"all (unlimited depth)", "current directory only", "1 level", "2 levels", "3 levels"};
|
||||
subDirCombo = new JComboBox<>(subDirOptions);
|
||||
recursiveCheckBox = new JCheckBox("", true); // keep for logic, but hidden/synced
|
||||
subDirCombo.addActionListener(e -> {
|
||||
recursiveCheckBox.setSelected(subDirCombo.getSelectedIndex() == 0);
|
||||
});
|
||||
northPanel.add(subDirCombo, gbc);
|
||||
|
||||
// Separator
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 5;
|
||||
gbc.gridwidth = 2;
|
||||
archiveSearchCheckBox = new JCheckBox("Search inside archives", false);
|
||||
archiveSearchCheckBox.setMnemonic(KeyEvent.VK_R);
|
||||
searchPanel.add(archiveSearchCheckBox, gbc);
|
||||
|
||||
gbc.gridwidth = 3;
|
||||
northPanel.add(new JSeparator(JSeparator.HORIZONTAL), gbc);
|
||||
|
||||
// Row 6: [x] Find text: [contentPatternCombo]
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 6;
|
||||
JLabel pathLabel = new JLabel("Directory: " + searchDirectory.getAbsolutePath());
|
||||
pathLabel.setFont(pathLabel.getFont().deriveFont(Font.ITALIC));
|
||||
searchPanel.add(pathLabel, gbc);
|
||||
gbc.gridwidth = 1;
|
||||
contentSearchCheckBox = new JCheckBox("Find text:");
|
||||
northPanel.add(contentSearchCheckBox, gbc);
|
||||
|
||||
add(searchPanel, BorderLayout.NORTH);
|
||||
gbc.gridx = 1;
|
||||
gbc.gridwidth = 2;
|
||||
gbc.weightx = 1.0;
|
||||
java.util.List<String> contentHistory = config != null ? config.getContentSearchHistory() : java.util.Collections.emptyList();
|
||||
contentPatternCombo = new JComboBox<>(new DefaultComboBoxModel<>(contentHistory.toArray(new String[0])));
|
||||
contentPatternCombo.setEditable(true);
|
||||
try { contentPatternCombo.setSelectedItem(""); contentPatternCombo.getEditor().setItem(""); } catch (Exception ignore) {}
|
||||
// Enable/disable based on checkbox
|
||||
contentPatternCombo.setEnabled(contentSearchCheckBox.isSelected());
|
||||
contentSearchCheckBox.addActionListener(e -> contentPatternCombo.setEnabled(contentSearchCheckBox.isSelected()));
|
||||
northPanel.add(contentPatternCombo, gbc);
|
||||
|
||||
// Tabulka s výsledky
|
||||
// Row 7 & 8: Options
|
||||
JPanel optionsPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0));
|
||||
|
||||
JPanel leftOptions = new JPanel(new GridLayout(0, 1));
|
||||
wholeWordCheckBox = new JCheckBox("Whole words only");
|
||||
caseSensitiveCheckBox = new JCheckBox("Case sensitive");
|
||||
regexContentCheckBox = new JCheckBox("RegEx (2)");
|
||||
leftOptions.add(wholeWordCheckBox);
|
||||
leftOptions.add(caseSensitiveCheckBox);
|
||||
leftOptions.add(regexContentCheckBox);
|
||||
|
||||
// Sync enabled state
|
||||
ActionListener contentSync = e -> {
|
||||
boolean sel = contentSearchCheckBox.isSelected();
|
||||
contentPatternCombo.setEnabled(sel);
|
||||
wholeWordCheckBox.setEnabled(sel);
|
||||
caseSensitiveCheckBox.setEnabled(sel);
|
||||
regexContentCheckBox.setEnabled(sel);
|
||||
};
|
||||
contentSearchCheckBox.addActionListener(contentSync);
|
||||
contentSync.actionPerformed(null);
|
||||
|
||||
optionsPanel.add(leftOptions);
|
||||
|
||||
gbc.gridx = 1;
|
||||
gbc.gridy = 7;
|
||||
gbc.gridwidth = 2;
|
||||
northPanel.add(optionsPanel, gbc);
|
||||
|
||||
add(northPanel, BorderLayout.NORTH);
|
||||
|
||||
// Tabulka s výsledky (CENTER panel)
|
||||
tableModel = new ResultsTableModel();
|
||||
resultsTable = new JTable(tableModel);
|
||||
resultsTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
resultsTable.setFont(new Font("Monospaced", Font.PLAIN, 12));
|
||||
// let the table columns adjust when the dialog is resized
|
||||
resultsTable.setAutoResizeMode(JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS);
|
||||
|
||||
resultsTable.getColumnModel().getColumn(0).setPreferredWidth(400);
|
||||
resultsTable.getColumnModel().getColumn(1).setPreferredWidth(100);
|
||||
resultsTable.getColumnModel().getColumn(2).setPreferredWidth(150);
|
||||
|
||||
// Double-click pro otevření umístění
|
||||
resultsTable.addMouseListener(new java.awt.event.MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(java.awt.event.MouseEvent e) {
|
||||
@ -229,7 +291,6 @@ public class SearchDialog extends JDialog {
|
||||
}
|
||||
});
|
||||
|
||||
// Enter pro otevření umístění
|
||||
resultsTable.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "openLocation");
|
||||
resultsTable.getActionMap().put("openLocation", new AbstractAction() {
|
||||
@Override
|
||||
@ -238,7 +299,6 @@ public class SearchDialog extends JDialog {
|
||||
}
|
||||
});
|
||||
|
||||
// Enable/disable view/edit buttons depending on selection
|
||||
resultsTable.getSelectionModel().addListSelectionListener(e -> {
|
||||
int sel = resultsTable.getSelectedRow();
|
||||
boolean ok = false;
|
||||
@ -255,67 +315,77 @@ public class SearchDialog extends JDialog {
|
||||
editButton.setEnabled(canEdit);
|
||||
});
|
||||
|
||||
JScrollPane scrollPane = new JScrollPane(resultsTable);
|
||||
scrollPane.setBorder(BorderFactory.createTitledBorder("Results"));
|
||||
add(scrollPane, BorderLayout.CENTER);
|
||||
JScrollPane scrollPane = new JScrollPane(resultsTable);
|
||||
scrollPane.setBorder(BorderFactory.createTitledBorder("Results"));
|
||||
add(scrollPane, BorderLayout.CENTER);
|
||||
|
||||
// Status bar with progress and message
|
||||
statusLabel = new JLabel("Ready");
|
||||
statusProgressBar = new JProgressBar();
|
||||
statusProgressBar.setVisible(false);
|
||||
statusProgressBar.setIndeterminate(false);
|
||||
JPanel statusPanel = new JPanel(new BorderLayout(6, 6));
|
||||
statusPanel.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
|
||||
statusPanel.add(statusLabel, BorderLayout.CENTER);
|
||||
statusPanel.add(statusProgressBar, BorderLayout.EAST);
|
||||
|
||||
// Panel with buttons
|
||||
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
|
||||
// Side Buttons (EAST panel)
|
||||
JPanel eastPanel = new JPanel();
|
||||
eastPanel.setLayout(new BoxLayout(eastPanel, BoxLayout.Y_AXIS));
|
||||
eastPanel.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 0));
|
||||
|
||||
searchButton = new JButton("Search");
|
||||
searchButton.addActionListener(e -> performSearch());
|
||||
|
||||
stopButton = new JButton("Cancel");
|
||||
stopButton.setEnabled(false);
|
||||
stopButton.addActionListener(e -> {
|
||||
searching = false;
|
||||
searchButton = new JButton("Start search");
|
||||
searchButton.setMaximumSize(new Dimension(120, 30));
|
||||
searchButton.addActionListener(e -> performSearch());
|
||||
|
||||
stopButton = new JButton("Cancel");
|
||||
stopButton.setMaximumSize(new Dimension(120, 30));
|
||||
stopButton.setEnabled(false);
|
||||
statusLabel.setText("Cancelling...");
|
||||
});
|
||||
|
||||
viewButton = new JButton("View");
|
||||
viewButton.setEnabled(false);
|
||||
viewButton.addActionListener(e -> viewSelectedFile());
|
||||
|
||||
editButton = new JButton("Edit");
|
||||
editButton.setEnabled(false);
|
||||
editButton.addActionListener(e -> editSelectedFile());
|
||||
|
||||
cancelButton = new JButton("Close");
|
||||
cancelButton.addActionListener(e -> {
|
||||
searching = false;
|
||||
dispose();
|
||||
});
|
||||
|
||||
JButton openButton = new JButton("Open location");
|
||||
openButton.addActionListener(e -> openSelectedFile());
|
||||
|
||||
buttonPanel.add(searchButton);
|
||||
buttonPanel.add(stopButton);
|
||||
buttonPanel.add(viewButton);
|
||||
buttonPanel.add(editButton);
|
||||
buttonPanel.add(openButton);
|
||||
buttonPanel.add(cancelButton);
|
||||
stopButton.addActionListener(e -> {
|
||||
searching = false;
|
||||
stopButton.setEnabled(false);
|
||||
statusLabel.setText("Cancelling...");
|
||||
});
|
||||
|
||||
// Compose bottom area: status bar above buttons
|
||||
JPanel bottomPanel = new JPanel(new BorderLayout());
|
||||
bottomPanel.add(statusPanel, BorderLayout.NORTH);
|
||||
bottomPanel.add(buttonPanel, BorderLayout.SOUTH);
|
||||
add(bottomPanel, BorderLayout.SOUTH);
|
||||
|
||||
// Set search button as default so Enter starts search
|
||||
getRootPane().setDefaultButton(searchButton);
|
||||
eastPanel.add(searchButton);
|
||||
eastPanel.add(Box.createVerticalStrut(5));
|
||||
eastPanel.add(stopButton);
|
||||
|
||||
add(eastPanel, BorderLayout.EAST);
|
||||
|
||||
// Status bar with progress and message
|
||||
statusLabel = new JLabel("Ready");
|
||||
statusProgressBar = new JProgressBar();
|
||||
statusProgressBar.setVisible(false);
|
||||
statusProgressBar.setIndeterminate(false);
|
||||
JPanel statusPanel = new JPanel(new BorderLayout(6, 6));
|
||||
statusPanel.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
|
||||
statusPanel.add(statusLabel, BorderLayout.CENTER);
|
||||
statusPanel.add(statusProgressBar, BorderLayout.EAST);
|
||||
|
||||
// Bottom buttons panel
|
||||
JPanel bottomButtonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
|
||||
|
||||
viewButton = new JButton("View");
|
||||
viewButton.setEnabled(false);
|
||||
viewButton.addActionListener(e -> viewSelectedFile());
|
||||
|
||||
editButton = new JButton("Edit");
|
||||
editButton.setEnabled(false);
|
||||
editButton.addActionListener(e -> editSelectedFile());
|
||||
|
||||
cancelButton = new JButton("Close");
|
||||
cancelButton.addActionListener(e -> {
|
||||
searching = false;
|
||||
dispose();
|
||||
});
|
||||
|
||||
JButton openButton = new JButton("Open location");
|
||||
openButton.addActionListener(e -> openSelectedFile());
|
||||
|
||||
bottomButtonPanel.add(viewButton);
|
||||
bottomButtonPanel.add(editButton);
|
||||
bottomButtonPanel.add(openButton);
|
||||
bottomButtonPanel.add(cancelButton);
|
||||
|
||||
JPanel bottomPanel = new JPanel(new BorderLayout());
|
||||
bottomPanel.add(statusPanel, BorderLayout.NORTH);
|
||||
bottomPanel.add(bottomButtonPanel, BorderLayout.SOUTH);
|
||||
add(bottomPanel, BorderLayout.SOUTH);
|
||||
|
||||
// Set search button as default so Enter starts search
|
||||
getRootPane().setDefaultButton(searchButton);
|
||||
|
||||
// Require explicit Enter to start search when choosing from history.
|
||||
// Bind Enter on the combo editor component so selecting an item from the
|
||||
// popup does not automatically trigger search until user confirms.
|
||||
@ -490,6 +560,12 @@ public class SearchDialog extends JDialog {
|
||||
namePat = it != null ? it.toString().trim() : "";
|
||||
} catch (Exception ex) { namePat = ""; }
|
||||
|
||||
String searchInDir = "";
|
||||
try {
|
||||
Object sit = searchInCombo.getEditor().getItem();
|
||||
searchInDir = sit != null ? sit.toString().trim() : "";
|
||||
} catch (Exception ex) { searchInDir = searchDirectory.getAbsolutePath(); }
|
||||
|
||||
String contentPat = "";
|
||||
try {
|
||||
Object cit = contentPatternCombo.getEditor().getItem();
|
||||
@ -505,7 +581,17 @@ public class SearchDialog extends JDialog {
|
||||
JOptionPane.WARNING_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
File currentSearchDir = new File(searchInDir);
|
||||
boolean searchEverything = everythingCheckBox != null && everythingCheckBox.isSelected();
|
||||
if (!searchEverything && (!currentSearchDir.exists() || !currentSearchDir.isDirectory())) {
|
||||
JOptionPane.showMessageDialog(this,
|
||||
"Zadaný adresář neexistuje",
|
||||
"Chyba",
|
||||
JOptionPane.WARNING_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isContentSearch && contentPat.isEmpty()) {
|
||||
JOptionPane.showMessageDialog(this,
|
||||
"Zadejte hledaný text pro vyhledávání v obsahu",
|
||||
@ -560,6 +646,18 @@ public class SearchDialog extends JDialog {
|
||||
final boolean searchArchives = archiveSearchCheckBox != null && archiveSearchCheckBox.isSelected();
|
||||
final boolean wholeWord = wholeWordCheckBox != null && wholeWordCheckBox.isSelected();
|
||||
final boolean caseSensitive = caseSensitiveCheckBox != null && caseSensitiveCheckBox.isSelected();
|
||||
final boolean filenameIsRegex = regexCheckBox != null && regexCheckBox.isSelected();
|
||||
final boolean contentIsRegex = regexContentCheckBox != null && regexContentCheckBox.isSelected();
|
||||
|
||||
int mDepth = -1;
|
||||
if (subDirCombo != null) {
|
||||
int idx = subDirCombo.getSelectedIndex();
|
||||
if (idx == 1) mDepth = 0;
|
||||
else if (idx == 2) mDepth = 1;
|
||||
else if (idx == 3) mDepth = 2;
|
||||
else if (idx == 4) mDepth = 3;
|
||||
}
|
||||
final int maxDepth = mDepth;
|
||||
|
||||
// Reset and show status
|
||||
foundCount = 0;
|
||||
@ -573,7 +671,7 @@ public class SearchDialog extends JDialog {
|
||||
|
||||
@Override
|
||||
protected Void doInBackground() throws Exception {
|
||||
FileOperations.search(searchDirectory, finalNamePat, finalContentPat, recursiveCheckBox.isSelected(), searchArchives, wholeWord, caseSensitive, new FileOperations.SearchCallback() {
|
||||
FileOperations.SearchCallback sc = new FileOperations.SearchCallback() {
|
||||
@Override
|
||||
public void onFileFound(File file, String virtualPath) {
|
||||
publish(new Object[]{"file", file, virtualPath});
|
||||
@ -588,7 +686,16 @@ public class SearchDialog extends JDialog {
|
||||
public void onProgress(String status) {
|
||||
publish(new Object[]{"progress", status});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
if (searchEverything) {
|
||||
for (File root : File.listRoots()) {
|
||||
if (!searching) break;
|
||||
FileOperations.search(root, finalNamePat, finalContentPat, maxDepth, searchArchives, wholeWord, caseSensitive, filenameIsRegex, contentIsRegex, sc);
|
||||
}
|
||||
} else {
|
||||
FileOperations.search(currentSearchDir, finalNamePat, finalContentPat, maxDepth, searchArchives, wholeWord, caseSensitive, filenameIsRegex, contentIsRegex, sc);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -720,7 +827,8 @@ public class SearchDialog extends JDialog {
|
||||
if (!contentPat.isEmpty()) {
|
||||
FileEditor.setLastSearchOptions(contentPat,
|
||||
wholeWordCheckBox != null && wholeWordCheckBox.isSelected(),
|
||||
caseSensitiveCheckBox != null && caseSensitiveCheckBox.isSelected());
|
||||
caseSensitiveCheckBox != null && caseSensitiveCheckBox.isSelected(),
|
||||
regexContentCheckBox != null && regexContentCheckBox.isSelected());
|
||||
}
|
||||
|
||||
FileEditor viewer = new FileEditor(owner, item.getFile(), vPath, config, true);
|
||||
@ -766,7 +874,8 @@ public class SearchDialog extends JDialog {
|
||||
if (!contentPat.isEmpty()) {
|
||||
FileEditor.setLastSearchOptions(contentPat,
|
||||
wholeWordCheckBox != null && wholeWordCheckBox.isSelected(),
|
||||
caseSensitiveCheckBox != null && caseSensitiveCheckBox.isSelected());
|
||||
caseSensitiveCheckBox != null && caseSensitiveCheckBox.isSelected(),
|
||||
regexContentCheckBox != null && regexContentCheckBox.isSelected());
|
||||
}
|
||||
|
||||
FileEditor editor = new FileEditor(owner, item.getFile(), config, false);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user