search results fixed

This commit is contained in:
rdavidek 2026-01-21 18:31:32 +01:00
parent 9b6b309bfe
commit 6854693492
3 changed files with 293 additions and 181 deletions

View File

@ -7,7 +7,6 @@ import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.zip.*; import java.util.zip.*;
@ -493,57 +492,57 @@ public class FileOperations {
} }
/** /**
* Search files by pattern * Search files by filename pattern and/or content text.
* If both are provided, both must match.
*/ */
public static void search(File directory, String pattern, boolean recursive, boolean searchArchives, SearchCallback callback) throws IOException { public static void search(File directory, String filenamePattern, String contentText, boolean recursive, boolean searchArchives, SearchCallback callback) throws IOException {
if (pattern == null) return;
// Prepare a compiled regex if the pattern contains wildcards to avoid recompiling per-file
Pattern filenameRegex = null; Pattern filenameRegex = null;
if (pattern.contains("*") || pattern.contains("?")) { String filenameLower = null;
String regex = pattern if (filenamePattern != null && !filenamePattern.isEmpty()) {
.replace(".", "\\.") filenameLower = filenamePattern.toLowerCase();
.replace("*", ".*") if (filenamePattern.contains("*") || filenamePattern.contains("?")) {
.replace("?", "."); String regex = filenamePattern
filenameRegex = Pattern.compile(regex, Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE); .replace(".", "\\.")
.replace("*", ".*")
.replace("?", ".");
filenameRegex = Pattern.compile(regex, Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE);
}
} }
searchRecursive(directory.toPath(), pattern.toLowerCase(), filenameRegex, recursive, searchArchives, callback);
Pattern contentPattern = null;
if (contentText != null && !contentText.isEmpty()) {
contentPattern = Pattern.compile(Pattern.quote(contentText), Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE);
}
searchRecursive(directory.toPath(), filenameLower, filenameRegex, contentPattern, recursive, searchArchives, callback);
} }
/** private static void searchRecursive(Path directory, String patternLower, Pattern filenameRegex, Pattern contentPattern, boolean recursive, boolean searchArchives, SearchCallback callback) throws IOException {
* Search file contents for a text fragment (case-insensitive).
* Calls callback.onFileFound(file) when a file contains the text.
*/
public static void searchContents(File directory, String text, boolean recursive, boolean searchArchives, SearchCallback callback) throws IOException {
if (text == null) return;
// Precompile a case-insensitive pattern for content search to avoid per-line lowercasing
Pattern contentPattern = Pattern.compile(Pattern.quote(text), Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE);
searchContentsRecursive(directory.toPath(), contentPattern, recursive, searchArchives, callback);
}
private static void searchRecursive(Path directory, String patternLower, Pattern filenameRegex, boolean recursive, boolean searchArchives, SearchCallback callback) throws IOException {
try (DirectoryStream<Path> stream = Files.newDirectoryStream(directory)) { try (DirectoryStream<Path> stream = Files.newDirectoryStream(directory)) {
for (Path entry : stream) { for (Path entry : stream) {
if (Files.isDirectory(entry)) { if (Files.isDirectory(entry)) {
if (recursive) { if (recursive) {
searchRecursive(entry, patternLower, filenameRegex, recursive, searchArchives, callback); searchRecursive(entry, patternLower, filenameRegex, contentPattern, recursive, searchArchives, callback);
} }
} else { } else {
File file = entry.toFile(); File file = entry.toFile();
String fileName = file.getName(); boolean nameMatched = true;
String fileNameLower = fileName.toLowerCase(); if (patternLower != null && !patternLower.isEmpty()) {
boolean matched = false; nameMatched = matchName(file.getName(), patternLower, filenameRegex);
if (fileNameLower.contains(patternLower)) matched = true;
else if (filenameRegex != null) {
Matcher m = filenameRegex.matcher(fileName);
if (m.matches()) matched = true;
} }
if (matched) {
boolean contentMatched = true;
if (nameMatched && contentPattern != null) {
contentMatched = fileMatchesContent(entry, contentPattern);
}
if (nameMatched && contentMatched) {
callback.onFileFound(file, null); callback.onFileFound(file, null);
} }
// SEARCH IN ARCHIVES // SEARCH IN ARCHIVES
if (searchArchives && isArchiveFile(file)) { if (searchArchives && isArchiveFile(file)) {
searchInArchive(file, fileNameLower, filenameRegex, callback); searchInArchiveCombined(file, patternLower, filenameRegex, contentPattern, callback);
} }
} }
} }
@ -552,39 +551,99 @@ public class FileOperations {
} }
} }
private static void searchContentsRecursive(Path directory, Pattern contentPattern, boolean recursive, boolean searchArchives, SearchCallback callback) throws IOException { private static boolean matchName(String name, String patternLower, Pattern filenameRegex) {
try (DirectoryStream<Path> stream = Files.newDirectoryStream(directory)) { String nameLower = name.toLowerCase();
for (Path entry : stream) { if (nameLower.contains(patternLower)) return true;
if (Files.isDirectory(entry)) { if (filenameRegex != null) {
if (recursive) { return filenameRegex.matcher(name).matches();
searchContentsRecursive(entry, contentPattern, recursive, searchArchives, callback); }
} return false;
} else { }
File file = entry.toFile();
// Try reading file as text line-by-line and search for pattern (case-insensitive via compiled Pattern)
try (BufferedReader br = Files.newBufferedReader(entry)) {
String line;
boolean found = false;
while ((line = br.readLine()) != null) {
if (contentPattern.matcher(line).find()) {
found = true;
break;
}
}
if (found) callback.onFileFound(file, null);
} catch (IOException ex) {
// Skip files that cannot be read as text
}
// SEARCH IN ARCHIVES CONTENTS private static boolean fileMatchesContent(Path entry, Pattern contentPattern) {
if (searchArchives && isArchiveFile(file)) { try (BufferedReader br = Files.newBufferedReader(entry)) {
searchContentsInArchive(file, contentPattern, callback); String line;
while ((line = br.readLine()) != null) {
if (contentPattern.matcher(line).find()) {
return true;
}
}
} catch (IOException ex) {
// Skip files that cannot be read as text
}
return false;
}
/**
* Reads the content of a file from an archive.
*/
public static byte[] readFileFromArchive(File archive, String entryPath) throws IOException {
String name = archive.getName().toLowerCase();
try {
if (name.endsWith(".zip") || name.endsWith(".jar")) {
try (ZipInputStream zis = new ZipInputStream(new FileInputStream(archive))) {
ZipEntry entry;
while ((entry = zis.getNextEntry()) != null) {
if (entry.getName().equals(entryPath) || (archive.getAbsolutePath() + File.separator + entry.getName()).equals(entryPath)) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[24576];
int len;
while ((len = zis.read(buffer)) > 0) {
baos.write(buffer, 0, len);
}
return baos.toByteArray();
}
}
}
} else if (name.endsWith(".tar.gz") || name.endsWith(".tgz") || name.endsWith(".tar")) {
InputStream is = new FileInputStream(archive);
if (name.endsWith(".gz") || name.endsWith(".tgz")) {
is = new org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream(is);
}
try (org.apache.commons.compress.archivers.tar.TarArchiveInputStream tais = new org.apache.commons.compress.archivers.tar.TarArchiveInputStream(is)) {
org.apache.commons.compress.archivers.tar.TarArchiveEntry entry;
while ((entry = tais.getNextTarEntry()) != null) {
if (entry.getName().equals(entryPath) || (archive.getAbsolutePath() + File.separator + entry.getName()).equals(entryPath)) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[24576];
int len;
while ((len = tais.read(buffer)) > 0) {
baos.write(buffer, 0, len);
}
return baos.toByteArray();
}
}
}
} else if (name.endsWith(".7z")) {
try (org.apache.commons.compress.archivers.sevenz.SevenZFile sevenZFile = new org.apache.commons.compress.archivers.sevenz.SevenZFile(archive)) {
org.apache.commons.compress.archivers.sevenz.SevenZArchiveEntry entry;
while ((entry = sevenZFile.getNextEntry()) != null) {
if (entry.getName().equals(entryPath) || (archive.getAbsolutePath() + File.separator + entry.getName()).equals(entryPath)) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[24576];
int len;
while ((len = sevenZFile.read(buffer)) > 0) {
baos.write(buffer, 0, len);
}
return baos.toByteArray();
}
}
}
} else if (name.endsWith(".rar")) {
try (com.github.junrar.Archive rar = new com.github.junrar.Archive(archive)) {
for (com.github.junrar.rarfile.FileHeader fh : rar.getFileHeaders()) {
if (fh.getFileName().equals(entryPath) || (archive.getAbsolutePath() + File.separator + fh.getFileName()).equals(entryPath)) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
rar.extractFile(fh, baos);
return baos.toByteArray();
}
} }
} }
} }
} catch (AccessDeniedException e) { } catch (Exception e) {
// Ignore directories without access throw new IOException("Failed to read from archive: " + e.getMessage(), e);
} }
throw new IOException("Entry not found in archive: " + entryPath);
} }
private static boolean isArchiveFile(File f) { private static boolean isArchiveFile(File f) {
@ -593,15 +652,25 @@ public class FileOperations {
return n.endsWith(".zip") || n.endsWith(".jar") || n.endsWith(".tar") || n.endsWith(".tar.gz") || n.endsWith(".tgz") || n.endsWith(".7z") || n.endsWith(".rar"); return 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 searchInArchive(File archive, String patternLower, Pattern filenameRegex, SearchCallback callback) { private static void searchInArchiveCombined(File archive, String patternLower, Pattern filenameRegex, Pattern contentPattern, SearchCallback callback) {
String name = archive.getName().toLowerCase(); String name = archive.getName().toLowerCase();
try { try {
if (name.endsWith(".zip") || name.endsWith(".jar")) { if (name.endsWith(".zip") || name.endsWith(".jar")) {
try (ZipInputStream zis = new ZipInputStream(new FileInputStream(archive))) { try (ZipInputStream zis = new ZipInputStream(new FileInputStream(archive))) {
ZipEntry entry; ZipEntry entry;
while ((entry = zis.getNextEntry()) != null) { while ((entry = zis.getNextEntry()) != null) {
if (matchEntry(entry.getName(), patternLower, filenameRegex)) { if (!entry.isDirectory()) {
callback.onFileFound(archive, archive.getAbsolutePath() + File.separator + entry.getName()); boolean nameMatched = true;
if (patternLower != null && !patternLower.isEmpty()) {
nameMatched = matchEntry(entry.getName(), patternLower, filenameRegex);
}
boolean contentMatched = true;
if (nameMatched && contentPattern != null) {
contentMatched = searchInStream(zis, contentPattern);
}
if (nameMatched && contentMatched) {
callback.onFileFound(archive, archive.getAbsolutePath() + File.separator + entry.getName());
}
} }
} }
} }
@ -613,54 +682,18 @@ public class FileOperations {
try (org.apache.commons.compress.archivers.tar.TarArchiveInputStream tais = new org.apache.commons.compress.archivers.tar.TarArchiveInputStream(is)) { try (org.apache.commons.compress.archivers.tar.TarArchiveInputStream tais = new org.apache.commons.compress.archivers.tar.TarArchiveInputStream(is)) {
org.apache.commons.compress.archivers.tar.TarArchiveEntry entry; org.apache.commons.compress.archivers.tar.TarArchiveEntry entry;
while ((entry = tais.getNextTarEntry()) != null) { while ((entry = tais.getNextTarEntry()) != null) {
if (matchEntry(entry.getName(), patternLower, filenameRegex)) { if (!entry.isDirectory()) {
callback.onFileFound(archive, archive.getAbsolutePath() + File.separator + entry.getName()); boolean nameMatched = true;
} if (patternLower != null && !patternLower.isEmpty()) {
} nameMatched = matchEntry(entry.getName(), patternLower, filenameRegex);
} }
} else if (name.endsWith(".7z")) { boolean contentMatched = true;
try (org.apache.commons.compress.archivers.sevenz.SevenZFile sevenZFile = new org.apache.commons.compress.archivers.sevenz.SevenZFile(archive)) { if (nameMatched && contentPattern != null) {
org.apache.commons.compress.archivers.sevenz.SevenZArchiveEntry entry; contentMatched = searchInStream(tais, contentPattern);
while ((entry = sevenZFile.getNextEntry()) != null) { }
if (matchEntry(entry.getName(), patternLower, filenameRegex)) { if (nameMatched && contentMatched) {
callback.onFileFound(archive, archive.getAbsolutePath() + File.separator + entry.getName()); callback.onFileFound(archive, archive.getAbsolutePath() + File.separator + entry.getName());
} }
}
}
} else if (name.endsWith(".rar")) {
try (com.github.junrar.Archive rar = new com.github.junrar.Archive(archive)) {
for (com.github.junrar.rarfile.FileHeader fh : rar.getFileHeaders()) {
if (matchEntry(fh.getFileName(), patternLower, filenameRegex)) {
callback.onFileFound(archive, archive.getAbsolutePath() + File.separator + fh.getFileName());
}
}
}
}
} catch (Exception ignore) {}
}
private static void searchContentsInArchive(File archive, Pattern contentPattern, SearchCallback callback) {
String name = archive.getName().toLowerCase();
try {
if (name.endsWith(".zip") || name.endsWith(".jar")) {
try (ZipInputStream zis = new ZipInputStream(new FileInputStream(archive))) {
ZipEntry entry;
while ((entry = zis.getNextEntry()) != null) {
if (!entry.isDirectory() && searchInStream(zis, contentPattern)) {
callback.onFileFound(archive, archive.getAbsolutePath() + File.separator + entry.getName());
}
}
}
} else if (name.endsWith(".tar.gz") || name.endsWith(".tgz") || name.endsWith(".tar")) {
InputStream is = new FileInputStream(archive);
if (name.endsWith(".gz") || name.endsWith(".tgz")) {
is = new org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream(is);
}
try (org.apache.commons.compress.archivers.tar.TarArchiveInputStream tais = new org.apache.commons.compress.archivers.tar.TarArchiveInputStream(is)) {
org.apache.commons.compress.archivers.tar.TarArchiveEntry entry;
while ((entry = tais.getNextTarEntry()) != null) {
if (!entry.isDirectory() && searchInStream(tais, contentPattern)) {
callback.onFileFound(archive, archive.getAbsolutePath() + File.separator + entry.getName());
} }
} }
} }
@ -669,17 +702,24 @@ public class FileOperations {
org.apache.commons.compress.archivers.sevenz.SevenZArchiveEntry entry; org.apache.commons.compress.archivers.sevenz.SevenZArchiveEntry entry;
while ((entry = sevenZFile.getNextEntry()) != null) { while ((entry = sevenZFile.getNextEntry()) != null) {
if (!entry.isDirectory()) { if (!entry.isDirectory()) {
// SevenZFile.read(buffer) reads from current entry boolean nameMatched = true;
if (searchInStream(new InputStream() { if (patternLower != null && !patternLower.isEmpty()) {
@Override nameMatched = matchEntry(entry.getName(), patternLower, filenameRegex);
public int read() throws IOException { }
return sevenZFile.read(); boolean contentMatched = true;
} if (nameMatched && contentPattern != null) {
@Override contentMatched = searchInStream(new InputStream() {
public int read(byte[] b, int off, int len) throws IOException { @Override
return sevenZFile.read(b, off, len); public int read() throws IOException {
} return sevenZFile.read();
}, contentPattern)) { }
@Override
public int read(byte[] b, int off, int len) throws IOException {
return sevenZFile.read(b, off, len);
}
}, contentPattern);
}
if (nameMatched && contentMatched) {
callback.onFileFound(archive, archive.getAbsolutePath() + File.separator + entry.getName()); callback.onFileFound(archive, archive.getAbsolutePath() + File.separator + entry.getName());
} }
} }
@ -689,9 +729,17 @@ public class FileOperations {
try (com.github.junrar.Archive rar = new com.github.junrar.Archive(archive)) { try (com.github.junrar.Archive rar = new com.github.junrar.Archive(archive)) {
for (com.github.junrar.rarfile.FileHeader fh : rar.getFileHeaders()) { for (com.github.junrar.rarfile.FileHeader fh : rar.getFileHeaders()) {
if (!fh.isDirectory()) { if (!fh.isDirectory()) {
ByteArrayOutputStream baos = new ByteArrayOutputStream(); boolean nameMatched = true;
rar.extractFile(fh, baos); if (patternLower != null && !patternLower.isEmpty()) {
if (contentPattern.matcher(new String(baos.toByteArray())).find()) { nameMatched = matchEntry(fh.getFileName(), patternLower, filenameRegex);
}
boolean contentMatched = true;
if (nameMatched && contentPattern != null) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
rar.extractFile(fh, baos);
contentMatched = contentPattern.matcher(new String(baos.toByteArray())).find();
}
if (nameMatched && contentMatched) {
callback.onFileFound(archive, archive.getAbsolutePath() + File.separator + fh.getFileName()); callback.onFileFound(archive, archive.getAbsolutePath() + File.separator + fh.getFileName());
} }
} }
@ -701,6 +749,7 @@ public class FileOperations {
} catch (Exception ignore) {} } catch (Exception ignore) {}
} }
private static boolean matchEntry(String entryName, String patternLower, Pattern filenameRegex) { private static boolean matchEntry(String entryName, String patternLower, Pattern filenameRegex) {
if (entryName == null) return false; if (entryName == null) return false;
String nameShort = entryName; String nameShort = entryName;

View File

@ -19,6 +19,7 @@ public class FileEditor extends JDialog {
private JTextArea textArea; private JTextArea textArea;
private JScrollPane scrollPane; private JScrollPane scrollPane;
private File file; private File file;
private String virtualPath;
private java.util.List<File> imageFiles = new java.util.ArrayList<>(); private java.util.List<File> imageFiles = new java.util.ArrayList<>();
private int currentImageIndex = -1; private int currentImageIndex = -1;
private AppConfig config; private AppConfig config;
@ -52,12 +53,17 @@ public class FileEditor extends JDialog {
private static String lastSearchValue = ""; private static String lastSearchValue = "";
public FileEditor(Window parent, File file, AppConfig config, boolean readOnly) { public FileEditor(Window parent, File file, AppConfig config, boolean readOnly) {
super(parent, (readOnly ? "Prohlížeč - " : "Editor - ") + file.getName(), ModalityType.MODELESS); this(parent, file, null, config, readOnly);
}
public FileEditor(Window parent, File file, String virtualPath, AppConfig config, boolean readOnly) {
super(parent, (readOnly ? "Prohlížeč - " : "Editor - ") + (virtualPath != null ? virtualPath.substring(virtualPath.lastIndexOf(File.separator) + 1) : file.getName()), ModalityType.MODELESS);
this.file = file; this.file = file;
this.virtualPath = virtualPath;
this.config = config; this.config = config;
this.readOnly = readOnly; this.readOnly = readOnly;
if (isImageFile(file)) { if (isImageFile(file) && virtualPath == null) {
initImageList(); initImageList();
} }
@ -809,6 +815,38 @@ public class FileEditor extends JDialog {
} }
private void loadFile() { private void loadFile() {
if (virtualPath != null) {
try {
fileBytes = cz.kamma.kfmanager.service.FileOperations.readFileFromArchive(file, virtualPath);
boolean binary = isBinary(fileBytes);
if (binary && readOnly) {
hexMode = true;
buildHexViewText(0L);
textArea.setEditable(false);
textArea.setFont(new Font("Monospaced", Font.PLAIN, textArea.getFont().getSize()));
ensureHexControls();
if (hexControlPanel.getParent() == null) northPanel.add(hexControlPanel);
hexControlPanel.setVisible(true);
northPanel.revalidate();
} else if (hexMode) {
buildHexViewText(0L);
} else {
String content = new String(fileBytes, "UTF-8");
textArea.setText(content);
textArea.setCaretPosition(0);
}
if (undoManager != null) undoManager.discardAllEdits();
modified = false;
updateTitle();
updateStatus();
return;
} catch (IOException e) {
JOptionPane.showMessageDialog(this, "Chyba při čtení z archivu: " + e.getMessage(), "Chyba", JOptionPane.ERROR_MESSAGE);
dispose();
return;
}
}
if (isImageFile(file)) { if (isImageFile(file)) {
loadImage(); loadImage();
return; return;

View File

@ -204,16 +204,30 @@ 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
public void actionPerformed(java.awt.event.ActionEvent e) {
openSelectedFile();
}
});
// Enable/disable view/edit buttons depending on selection // Enable/disable view/edit buttons depending on selection
resultsTable.getSelectionModel().addListSelectionListener(e -> { resultsTable.getSelectionModel().addListSelectionListener(e -> {
int sel = resultsTable.getSelectedRow(); int sel = resultsTable.getSelectedRow();
boolean ok = false; boolean ok = false;
boolean canEdit = false;
if (sel >= 0) { if (sel >= 0) {
FileItem it = tableModel.getResult(sel); FileItem it = tableModel.getResult(sel);
ok = it != null && !it.isDirectory() && !"..".equals(it.getName()); ok = it != null && !it.isDirectory() && !"..".equals(it.getName());
if (ok) {
boolean inArchive = it.getPath() != null && !it.getPath().equals(it.getFile().getAbsolutePath());
canEdit = !inArchive;
}
} }
viewButton.setEnabled(ok); viewButton.setEnabled(ok);
editButton.setEnabled(ok); editButton.setEnabled(canEdit);
}); });
JScrollPane scrollPane = new JScrollPane(resultsTable); JScrollPane scrollPane = new JScrollPane(resultsTable);
@ -261,6 +275,9 @@ public class SearchDialog extends JDialog {
bottomPanel.add(statusPanel, BorderLayout.NORTH); bottomPanel.add(statusPanel, BorderLayout.NORTH);
bottomPanel.add(buttonPanel, BorderLayout.SOUTH); bottomPanel.add(buttonPanel, BorderLayout.SOUTH);
add(bottomPanel, 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. // Require explicit Enter to start search when choosing from history.
// Bind Enter on the combo editor component so selecting an item from the // Bind Enter on the combo editor component so selecting an item from the
@ -273,17 +290,16 @@ public class SearchDialog extends JDialog {
@Override @Override
public void actionPerformed(java.awt.event.ActionEvent e) { public void actionPerformed(java.awt.event.ActionEvent e) {
try { try {
// If the popup is visible, treat Enter as "confirm selection": fill editor and close popup // If the popup is visible, treat Enter as "confirm selection" and proceed to search
if (patternCombo.isPopupVisible()) { if (patternCombo.isPopupVisible()) {
Object sel = patternCombo.getSelectedItem(); Object sel = patternCombo.getSelectedItem();
if (sel != null) { if (sel != null) {
patternCombo.getEditor().setItem(sel.toString()); patternCombo.getEditor().setItem(sel.toString());
} }
patternCombo.hidePopup(); patternCombo.hidePopup();
return; // do not start search yet
} }
} catch (Exception ignore) {} } catch (Exception ignore) {}
// Popup not visible -> actual confirm to start search // Actual confirm to start search
performSearch(); performSearch();
} }
}); });
@ -303,7 +319,6 @@ public class SearchDialog extends JDialog {
Object sel = contentPatternCombo.getSelectedItem(); Object sel = contentPatternCombo.getSelectedItem();
if (sel != null) contentPatternCombo.getEditor().setItem(sel.toString()); if (sel != null) contentPatternCombo.getEditor().setItem(sel.toString());
contentPatternCombo.hidePopup(); contentPatternCombo.hidePopup();
return;
} }
} catch (Exception ignore) {} } catch (Exception ignore) {}
performSearch(); performSearch();
@ -446,62 +461,64 @@ public class SearchDialog extends JDialog {
final boolean isContentSearch = contentSearchCheckBox != null && contentSearchCheckBox.isSelected(); final boolean isContentSearch = contentSearchCheckBox != null && contentSearchCheckBox.isSelected();
if (isContentSearch) { if (namePat.isEmpty() && (!isContentSearch || contentPat.isEmpty())) {
if (contentPat.isEmpty()) { JOptionPane.showMessageDialog(this,
JOptionPane.showMessageDialog(this, "Zadejte hledaný vzor nebo text",
"Zadejte hledaný text", "Chyba",
"Chyba", JOptionPane.WARNING_MESSAGE);
JOptionPane.WARNING_MESSAGE); return;
return; }
}
} else { if (isContentSearch && contentPat.isEmpty()) {
if (namePat.isEmpty()) { JOptionPane.showMessageDialog(this,
JOptionPane.showMessageDialog(this, "Zadejte hledaný text pro vyhledávání v obsahu",
"Zadejte hledaný vzor", "Chyba",
"Chyba", JOptionPane.WARNING_MESSAGE);
JOptionPane.WARNING_MESSAGE); return;
return;
}
} }
tableModel.clear(); tableModel.clear();
searchButton.setEnabled(false); searchButton.setEnabled(false);
searching = true; searching = true;
// Persist the chosen pattern into the appropriate history (most-recent-first) // Persist the chosen patterns into history
if (config != null) { if (config != null) {
try { try {
if (isContentSearch) { if (!namePat.isEmpty()) {
java.util.List<String> chist = new java.util.ArrayList<>(config.getContentSearchHistory());
chist.remove(contentPat);
chist.add(0, contentPat);
int max = 20;
while (chist.size() > max) chist.remove(chist.size() - 1);
config.saveContentSearchHistory(chist);
config.saveConfig();
// update content combo model
javax.swing.DefaultComboBoxModel<String> cm = (javax.swing.DefaultComboBoxModel<String>) contentPatternCombo.getModel();
cm.removeAllElements();
for (String s : chist) cm.addElement(s);
contentPatternCombo.setSelectedItem(contentPat);
} else {
java.util.List<String> hist = new java.util.ArrayList<>(config.getSearchHistory()); java.util.List<String> hist = new java.util.ArrayList<>(config.getSearchHistory());
hist.remove(namePat); hist.remove(namePat);
hist.add(0, namePat); hist.add(0, namePat);
int max = 20; int max = 20;
while (hist.size() > max) hist.remove(hist.size() - 1); while (hist.size() > max) hist.remove(hist.size() - 1);
config.saveSearchHistory(hist); config.saveSearchHistory(hist);
config.saveConfig();
// update combo model // update combo model
javax.swing.DefaultComboBoxModel<String> m = (javax.swing.DefaultComboBoxModel<String>) patternCombo.getModel(); javax.swing.DefaultComboBoxModel<String> m = (javax.swing.DefaultComboBoxModel<String>) patternCombo.getModel();
m.removeAllElements(); m.removeAllElements();
for (String s : hist) m.addElement(s); for (String s : hist) m.addElement(s);
patternCombo.setSelectedItem(namePat); patternCombo.setSelectedItem(namePat);
} }
if (isContentSearch && !contentPat.isEmpty()) {
java.util.List<String> chist = new java.util.ArrayList<>(config.getContentSearchHistory());
chist.remove(contentPat);
chist.add(0, contentPat);
int max = 20;
while (chist.size() > max) chist.remove(chist.size() - 1);
config.saveContentSearchHistory(chist);
// update content combo model
javax.swing.DefaultComboBoxModel<String> cm = (javax.swing.DefaultComboBoxModel<String>) contentPatternCombo.getModel();
cm.removeAllElements();
for (String s : chist) cm.addElement(s);
contentPatternCombo.setSelectedItem(contentPat);
}
config.saveConfig();
} catch (Exception ignore) {} } catch (Exception ignore) {}
} }
final String pattern = isContentSearch ? contentPat : namePat; final String finalNamePat = namePat;
final String finalContentPat = isContentSearch ? contentPat : null;
final boolean searchArchives = archiveSearchCheckBox != null && archiveSearchCheckBox.isSelected(); final boolean searchArchives = archiveSearchCheckBox != null && archiveSearchCheckBox.isSelected();
// Reset and show status // Reset and show status
@ -514,17 +531,10 @@ public class SearchDialog extends JDialog {
SwingWorker<Void, Object[]> worker = new SwingWorker<Void, Object[]>() { SwingWorker<Void, Object[]> worker = new SwingWorker<Void, Object[]>() {
@Override @Override
protected Void doInBackground() throws Exception { protected Void doInBackground() throws Exception {
if (isContentSearch) { FileOperations.search(searchDirectory, finalNamePat, finalContentPat, recursiveCheckBox.isSelected(), searchArchives, (file, virtualPath) -> {
FileOperations.searchContents(searchDirectory, pattern, recursiveCheckBox.isSelected(), searchArchives, (file, virtualPath) -> { if (!searching) return;
if (!searching) return; publish(new Object[]{file, virtualPath});
publish(new Object[]{file, virtualPath}); });
});
} else {
FileOperations.search(searchDirectory, pattern, recursiveCheckBox.isSelected(), searchArchives, (file, virtualPath) -> {
if (!searching) return;
publish(new Object[]{file, virtualPath});
});
}
return null; return null;
} }
@ -613,11 +623,18 @@ public class SearchDialog extends JDialog {
if (item != null && !item.isDirectory() && !"..".equals(item.getName())) { if (item != null && !item.isDirectory() && !"..".equals(item.getName())) {
try { try {
Frame owner = (Frame) SwingUtilities.getWindowAncestor(this); Frame owner = (Frame) SwingUtilities.getWindowAncestor(this);
FileEditor viewer = new FileEditor(owner, item.getFile(), config, true); String vPath = null;
if (item.getPath() != null && !item.getPath().equals(item.getFile().getAbsolutePath())) {
vPath = item.getPath();
}
FileEditor viewer = new FileEditor(owner, item.getFile(), vPath, config, true);
viewer.addWindowListener(new java.awt.event.WindowAdapter() { viewer.addWindowListener(new java.awt.event.WindowAdapter() {
@Override @Override
public void windowClosed(java.awt.event.WindowEvent e) { public void windowClosed(java.awt.event.WindowEvent e) {
resultsTable.requestFocusInWindow(); SwingUtilities.invokeLater(() -> {
SearchDialog.this.toFront();
resultsTable.requestFocusInWindow();
});
} }
}); });
viewer.setVisible(true); viewer.setVisible(true);
@ -636,13 +653,21 @@ public class SearchDialog extends JDialog {
if (sel >= 0) { if (sel >= 0) {
FileItem item = tableModel.getResult(sel); FileItem item = tableModel.getResult(sel);
if (item != null && !item.isDirectory() && !"..".equals(item.getName())) { if (item != null && !item.isDirectory() && !"..".equals(item.getName())) {
boolean inArchive = item.getPath() != null && !item.getPath().equals(item.getFile().getAbsolutePath());
if (inArchive) {
JOptionPane.showMessageDialog(this, "Editace souborů přímo v archivu není podporována. Použijte 'Otevřít umístění' pro přístup k archivu.", "Informace", JOptionPane.INFORMATION_MESSAGE);
return;
}
try { try {
Frame owner = (Frame) SwingUtilities.getWindowAncestor(this); Frame owner = (Frame) SwingUtilities.getWindowAncestor(this);
FileEditor editor = new FileEditor(owner, item.getFile(), config, false); FileEditor editor = new FileEditor(owner, item.getFile(), config, false);
editor.addWindowListener(new java.awt.event.WindowAdapter() { editor.addWindowListener(new java.awt.event.WindowAdapter() {
@Override @Override
public void windowClosed(java.awt.event.WindowEvent e) { public void windowClosed(java.awt.event.WindowEvent e) {
resultsTable.requestFocusInWindow(); SwingUtilities.invokeLater(() -> {
SearchDialog.this.toFront();
resultsTable.requestFocusInWindow();
});
} }
}); });
editor.setVisible(true); editor.setVisible(true);