From 685469349290c5457a0d3bdb821f44e1a86e8488 Mon Sep 17 00:00:00 2001 From: rdavidek Date: Wed, 21 Jan 2026 18:31:32 +0100 Subject: [PATCH] search results fixed --- .../kfmanager/service/FileOperations.java | 303 ++++++++++-------- .../cz/kamma/kfmanager/ui/FileEditor.java | 42 ++- .../cz/kamma/kfmanager/ui/SearchDialog.java | 129 +++++--- 3 files changed, 293 insertions(+), 181 deletions(-) diff --git a/src/main/java/cz/kamma/kfmanager/service/FileOperations.java b/src/main/java/cz/kamma/kfmanager/service/FileOperations.java index 2d711d3..c90278c 100644 --- a/src/main/java/cz/kamma/kfmanager/service/FileOperations.java +++ b/src/main/java/cz/kamma/kfmanager/service/FileOperations.java @@ -7,7 +7,6 @@ import java.nio.file.*; import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; import java.util.List; -import java.util.regex.Matcher; import java.util.regex.Pattern; 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 { - if (pattern == null) return; - // Prepare a compiled regex if the pattern contains wildcards to avoid recompiling per-file + public static void search(File directory, String filenamePattern, String contentText, boolean recursive, boolean searchArchives, SearchCallback callback) throws IOException { Pattern filenameRegex = null; - if (pattern.contains("*") || pattern.contains("?")) { - String regex = pattern - .replace(".", "\\.") - .replace("*", ".*") - .replace("?", "."); - filenameRegex = Pattern.compile(regex, Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE); + 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); + } } - 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); } - /** - * 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 { + private static void searchRecursive(Path directory, String patternLower, Pattern filenameRegex, Pattern contentPattern, boolean recursive, boolean searchArchives, SearchCallback callback) throws IOException { try (DirectoryStream stream = Files.newDirectoryStream(directory)) { for (Path entry : stream) { if (Files.isDirectory(entry)) { if (recursive) { - searchRecursive(entry, patternLower, filenameRegex, recursive, searchArchives, callback); + searchRecursive(entry, patternLower, filenameRegex, contentPattern, recursive, searchArchives, callback); } } else { File file = entry.toFile(); - String fileName = file.getName(); - String fileNameLower = fileName.toLowerCase(); - boolean matched = false; - if (fileNameLower.contains(patternLower)) matched = true; - else if (filenameRegex != null) { - Matcher m = filenameRegex.matcher(fileName); - if (m.matches()) matched = true; + boolean nameMatched = true; + if (patternLower != null && !patternLower.isEmpty()) { + nameMatched = matchName(file.getName(), patternLower, filenameRegex); } - if (matched) { + + boolean contentMatched = true; + if (nameMatched && contentPattern != null) { + contentMatched = fileMatchesContent(entry, contentPattern); + } + + if (nameMatched && contentMatched) { callback.onFileFound(file, null); } // SEARCH IN ARCHIVES 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 { - try (DirectoryStream stream = Files.newDirectoryStream(directory)) { - for (Path entry : stream) { - if (Files.isDirectory(entry)) { - if (recursive) { - searchContentsRecursive(entry, contentPattern, recursive, searchArchives, callback); - } - } 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 - } + private static boolean matchName(String name, String patternLower, Pattern filenameRegex) { + String nameLower = name.toLowerCase(); + if (nameLower.contains(patternLower)) return true; + if (filenameRegex != null) { + return filenameRegex.matcher(name).matches(); + } + return false; + } - // SEARCH IN ARCHIVES CONTENTS - if (searchArchives && isArchiveFile(file)) { - searchContentsInArchive(file, contentPattern, callback); + private static boolean fileMatchesContent(Path entry, Pattern contentPattern) { + try (BufferedReader br = Files.newBufferedReader(entry)) { + 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) { - // Ignore directories without access + } catch (Exception e) { + 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) { @@ -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"); } - 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(); try { if (name.endsWith(".zip") || name.endsWith(".jar")) { try (ZipInputStream zis = new ZipInputStream(new FileInputStream(archive))) { ZipEntry entry; while ((entry = zis.getNextEntry()) != null) { - if (matchEntry(entry.getName(), patternLower, filenameRegex)) { - callback.onFileFound(archive, archive.getAbsolutePath() + File.separator + entry.getName()); + if (!entry.isDirectory()) { + 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)) { org.apache.commons.compress.archivers.tar.TarArchiveEntry entry; while ((entry = tais.getNextTarEntry()) != null) { - if (matchEntry(entry.getName(), patternLower, filenameRegex)) { - callback.onFileFound(archive, archive.getAbsolutePath() + File.separator + entry.getName()); - } - } - } - } 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 (matchEntry(entry.getName(), patternLower, filenameRegex)) { - 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()); + if (!entry.isDirectory()) { + boolean nameMatched = true; + if (patternLower != null && !patternLower.isEmpty()) { + nameMatched = matchEntry(entry.getName(), patternLower, filenameRegex); + } + boolean contentMatched = true; + if (nameMatched && contentPattern != null) { + contentMatched = searchInStream(tais, contentPattern); + } + if (nameMatched && contentMatched) { + callback.onFileFound(archive, archive.getAbsolutePath() + File.separator + entry.getName()); + } } } } @@ -669,17 +702,24 @@ public class FileOperations { org.apache.commons.compress.archivers.sevenz.SevenZArchiveEntry entry; while ((entry = sevenZFile.getNextEntry()) != null) { if (!entry.isDirectory()) { - // SevenZFile.read(buffer) reads from current entry - if (searchInStream(new InputStream() { - @Override - public int read() throws IOException { - return sevenZFile.read(); - } - @Override - public int read(byte[] b, int off, int len) throws IOException { - return sevenZFile.read(b, off, len); - } - }, contentPattern)) { + boolean nameMatched = true; + if (patternLower != null && !patternLower.isEmpty()) { + nameMatched = matchEntry(entry.getName(), patternLower, filenameRegex); + } + boolean contentMatched = true; + if (nameMatched && contentPattern != null) { + contentMatched = searchInStream(new InputStream() { + @Override + public int read() throws IOException { + return sevenZFile.read(); + } + @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()); } } @@ -689,9 +729,17 @@ public class FileOperations { try (com.github.junrar.Archive rar = new com.github.junrar.Archive(archive)) { for (com.github.junrar.rarfile.FileHeader fh : rar.getFileHeaders()) { if (!fh.isDirectory()) { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - rar.extractFile(fh, baos); - if (contentPattern.matcher(new String(baos.toByteArray())).find()) { + boolean nameMatched = true; + if (patternLower != null && !patternLower.isEmpty()) { + 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()); } } @@ -701,6 +749,7 @@ public class FileOperations { } catch (Exception ignore) {} } + private static boolean matchEntry(String entryName, String patternLower, Pattern filenameRegex) { if (entryName == null) return false; String nameShort = entryName; diff --git a/src/main/java/cz/kamma/kfmanager/ui/FileEditor.java b/src/main/java/cz/kamma/kfmanager/ui/FileEditor.java index 64aca3e..15fbf59 100644 --- a/src/main/java/cz/kamma/kfmanager/ui/FileEditor.java +++ b/src/main/java/cz/kamma/kfmanager/ui/FileEditor.java @@ -19,6 +19,7 @@ public class FileEditor extends JDialog { private JTextArea textArea; private JScrollPane scrollPane; private File file; + private String virtualPath; private java.util.List imageFiles = new java.util.ArrayList<>(); private int currentImageIndex = -1; private AppConfig config; @@ -52,12 +53,17 @@ public class FileEditor extends JDialog { private static String lastSearchValue = ""; 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.virtualPath = virtualPath; this.config = config; this.readOnly = readOnly; - if (isImageFile(file)) { + if (isImageFile(file) && virtualPath == null) { initImageList(); } @@ -809,6 +815,38 @@ public class FileEditor extends JDialog { } 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)) { loadImage(); return; diff --git a/src/main/java/cz/kamma/kfmanager/ui/SearchDialog.java b/src/main/java/cz/kamma/kfmanager/ui/SearchDialog.java index 7088009..419982d 100644 --- a/src/main/java/cz/kamma/kfmanager/ui/SearchDialog.java +++ b/src/main/java/cz/kamma/kfmanager/ui/SearchDialog.java @@ -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 resultsTable.getSelectionModel().addListSelectionListener(e -> { int sel = resultsTable.getSelectedRow(); boolean ok = false; + boolean canEdit = false; if (sel >= 0) { FileItem it = tableModel.getResult(sel); 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); - editButton.setEnabled(ok); + editButton.setEnabled(canEdit); }); JScrollPane scrollPane = new JScrollPane(resultsTable); @@ -261,6 +275,9 @@ public class SearchDialog extends JDialog { 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); // Require explicit Enter to start search when choosing from history. // Bind Enter on the combo editor component so selecting an item from the @@ -273,17 +290,16 @@ public class SearchDialog extends JDialog { @Override public void actionPerformed(java.awt.event.ActionEvent e) { 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()) { Object sel = patternCombo.getSelectedItem(); if (sel != null) { patternCombo.getEditor().setItem(sel.toString()); } patternCombo.hidePopup(); - return; // do not start search yet } } catch (Exception ignore) {} - // Popup not visible -> actual confirm to start search + // Actual confirm to start search performSearch(); } }); @@ -303,7 +319,6 @@ public class SearchDialog extends JDialog { Object sel = contentPatternCombo.getSelectedItem(); if (sel != null) contentPatternCombo.getEditor().setItem(sel.toString()); contentPatternCombo.hidePopup(); - return; } } catch (Exception ignore) {} performSearch(); @@ -446,62 +461,64 @@ public class SearchDialog extends JDialog { final boolean isContentSearch = contentSearchCheckBox != null && contentSearchCheckBox.isSelected(); - if (isContentSearch) { - if (contentPat.isEmpty()) { - JOptionPane.showMessageDialog(this, - "Zadejte hledaný text", - "Chyba", - JOptionPane.WARNING_MESSAGE); - return; - } - } else { - if (namePat.isEmpty()) { - JOptionPane.showMessageDialog(this, - "Zadejte hledaný vzor", - "Chyba", - JOptionPane.WARNING_MESSAGE); - return; - } + if (namePat.isEmpty() && (!isContentSearch || contentPat.isEmpty())) { + JOptionPane.showMessageDialog(this, + "Zadejte hledaný vzor nebo text", + "Chyba", + JOptionPane.WARNING_MESSAGE); + return; + } + + if (isContentSearch && contentPat.isEmpty()) { + JOptionPane.showMessageDialog(this, + "Zadejte hledaný text pro vyhledávání v obsahu", + "Chyba", + JOptionPane.WARNING_MESSAGE); + return; } tableModel.clear(); searchButton.setEnabled(false); searching = true; - // Persist the chosen pattern into the appropriate history (most-recent-first) + // Persist the chosen patterns into history if (config != null) { try { - if (isContentSearch) { - java.util.List 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 cm = (javax.swing.DefaultComboBoxModel) contentPatternCombo.getModel(); - cm.removeAllElements(); - for (String s : chist) cm.addElement(s); - contentPatternCombo.setSelectedItem(contentPat); - } else { + if (!namePat.isEmpty()) { java.util.List hist = new java.util.ArrayList<>(config.getSearchHistory()); hist.remove(namePat); hist.add(0, namePat); int max = 20; while (hist.size() > max) hist.remove(hist.size() - 1); config.saveSearchHistory(hist); - config.saveConfig(); + // update combo model javax.swing.DefaultComboBoxModel m = (javax.swing.DefaultComboBoxModel) patternCombo.getModel(); m.removeAllElements(); for (String s : hist) m.addElement(s); patternCombo.setSelectedItem(namePat); } + + if (isContentSearch && !contentPat.isEmpty()) { + java.util.List 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 cm = (javax.swing.DefaultComboBoxModel) contentPatternCombo.getModel(); + cm.removeAllElements(); + for (String s : chist) cm.addElement(s); + contentPatternCombo.setSelectedItem(contentPat); + } + config.saveConfig(); } 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(); // Reset and show status @@ -514,17 +531,10 @@ public class SearchDialog extends JDialog { SwingWorker worker = new SwingWorker() { @Override protected Void doInBackground() throws Exception { - if (isContentSearch) { - FileOperations.searchContents(searchDirectory, pattern, recursiveCheckBox.isSelected(), searchArchives, (file, virtualPath) -> { - if (!searching) return; - publish(new Object[]{file, virtualPath}); - }); - } else { - FileOperations.search(searchDirectory, pattern, recursiveCheckBox.isSelected(), searchArchives, (file, virtualPath) -> { - if (!searching) return; - publish(new Object[]{file, virtualPath}); - }); - } + FileOperations.search(searchDirectory, finalNamePat, finalContentPat, recursiveCheckBox.isSelected(), searchArchives, (file, virtualPath) -> { + if (!searching) return; + publish(new Object[]{file, virtualPath}); + }); return null; } @@ -613,11 +623,18 @@ public class SearchDialog extends JDialog { if (item != null && !item.isDirectory() && !"..".equals(item.getName())) { try { 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() { @Override public void windowClosed(java.awt.event.WindowEvent e) { - resultsTable.requestFocusInWindow(); + SwingUtilities.invokeLater(() -> { + SearchDialog.this.toFront(); + resultsTable.requestFocusInWindow(); + }); } }); viewer.setVisible(true); @@ -636,13 +653,21 @@ public class SearchDialog extends JDialog { if (sel >= 0) { FileItem item = tableModel.getResult(sel); 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 { Frame owner = (Frame) SwingUtilities.getWindowAncestor(this); FileEditor editor = new FileEditor(owner, item.getFile(), config, false); editor.addWindowListener(new java.awt.event.WindowAdapter() { @Override public void windowClosed(java.awt.event.WindowEvent e) { - resultsTable.requestFocusInWindow(); + SwingUtilities.invokeLater(() -> { + SearchDialog.this.toFront(); + resultsTable.requestFocusInWindow(); + }); } }); editor.setVisible(true);