added new functionality
This commit is contained in:
parent
7b07d98dab
commit
e543320f97
@ -334,4 +334,109 @@ public class AppConfig {
|
|||||||
frame.setExtendedState(Frame.MAXIMIZED_BOTH);
|
frame.setExtendedState(Frame.MAXIMIZED_BOTH);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- Search dialog persistence ---
|
||||||
|
public int getSearchDialogX() {
|
||||||
|
return Integer.parseInt(properties.getProperty("searchDialog.x", "100"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSearchDialogX(int x) {
|
||||||
|
properties.setProperty("searchDialog.x", String.valueOf(x));
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSearchDialogY() {
|
||||||
|
return Integer.parseInt(properties.getProperty("searchDialog.y", "100"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSearchDialogY(int y) {
|
||||||
|
properties.setProperty("searchDialog.y", String.valueOf(y));
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSearchDialogWidth() {
|
||||||
|
return Integer.parseInt(properties.getProperty("searchDialog.width", "700"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSearchDialogWidth(int w) {
|
||||||
|
properties.setProperty("searchDialog.width", String.valueOf(w));
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSearchDialogHeight() {
|
||||||
|
return Integer.parseInt(properties.getProperty("searchDialog.height", "500"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSearchDialogHeight(int h) {
|
||||||
|
properties.setProperty("searchDialog.height", String.valueOf(h));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Save search dialog bounds */
|
||||||
|
public void saveSearchDialogState(Window win) {
|
||||||
|
if (win == null) return;
|
||||||
|
setSearchDialogX(win.getX());
|
||||||
|
setSearchDialogY(win.getY());
|
||||||
|
setSearchDialogWidth(win.getWidth());
|
||||||
|
setSearchDialogHeight(win.getHeight());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Restore search dialog bounds */
|
||||||
|
public void restoreSearchDialogState(Window win) {
|
||||||
|
if (win == null) return;
|
||||||
|
win.setLocation(getSearchDialogX(), getSearchDialogY());
|
||||||
|
win.setSize(getSearchDialogWidth(), getSearchDialogHeight());
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Search history persistence ---
|
||||||
|
public java.util.List<String> getSearchHistory() {
|
||||||
|
java.util.List<String> list = new java.util.ArrayList<>();
|
||||||
|
int count = Integer.parseInt(properties.getProperty("search.history.count", "0"));
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
String v = properties.getProperty("search.history." + i, null);
|
||||||
|
if (v != null && !v.isEmpty()) list.add(v);
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void saveSearchHistory(java.util.List<String> history) {
|
||||||
|
if (history == null) {
|
||||||
|
properties.setProperty("search.history.count", "0");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int limit = Math.min(history.size(), 50); // cap stored entries
|
||||||
|
properties.setProperty("search.history.count", String.valueOf(limit));
|
||||||
|
for (int i = 0; i < limit; i++) {
|
||||||
|
properties.setProperty("search.history." + i, history.get(i));
|
||||||
|
}
|
||||||
|
// remove any old entries beyond limit
|
||||||
|
int old = Integer.parseInt(properties.getProperty("search.history.count", "0"));
|
||||||
|
for (int i = limit; i < old; i++) {
|
||||||
|
properties.remove("search.history." + i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Content search history persistence ---
|
||||||
|
public java.util.List<String> getContentSearchHistory() {
|
||||||
|
java.util.List<String> list = new java.util.ArrayList<>();
|
||||||
|
int count = Integer.parseInt(properties.getProperty("search.content.history.count", "0"));
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
String v = properties.getProperty("search.content.history." + i, null);
|
||||||
|
if (v != null && !v.isEmpty()) list.add(v);
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void saveContentSearchHistory(java.util.List<String> history) {
|
||||||
|
if (history == null) {
|
||||||
|
properties.setProperty("search.content.history.count", "0");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int limit = Math.min(history.size(), 50);
|
||||||
|
properties.setProperty("search.content.history.count", String.valueOf(limit));
|
||||||
|
for (int i = 0; i < limit; i++) {
|
||||||
|
properties.setProperty("search.content.history." + i, history.get(i));
|
||||||
|
}
|
||||||
|
// remove old entries beyond limit
|
||||||
|
int old = Integer.parseInt(properties.getProperty("search.content.history.count", "0"));
|
||||||
|
for (int i = limit; i < old; i++) {
|
||||||
|
properties.remove("search.content.history." + i);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,6 +6,8 @@ import java.io.*;
|
|||||||
import java.nio.file.*;
|
import java.nio.file.*;
|
||||||
import java.nio.file.attribute.BasicFileAttributes;
|
import java.nio.file.attribute.BasicFileAttributes;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service for file operations - copy, move, delete, etc.
|
* Service for file operations - copy, move, delete, etc.
|
||||||
@ -156,19 +158,47 @@ public class FileOperations {
|
|||||||
* Search files by pattern
|
* Search files by pattern
|
||||||
*/
|
*/
|
||||||
public static void search(File directory, String pattern, boolean recursive, SearchCallback callback) throws IOException {
|
public static void search(File directory, String pattern, boolean recursive, SearchCallback callback) throws IOException {
|
||||||
searchRecursive(directory.toPath(), pattern.toLowerCase(), recursive, callback);
|
if (pattern == null) return;
|
||||||
|
// Prepare a compiled regex if the pattern contains wildcards to avoid recompiling per-file
|
||||||
|
Pattern filenameRegex = null;
|
||||||
|
if (pattern.contains("*") || pattern.contains("?")) {
|
||||||
|
String regex = pattern
|
||||||
|
.replace(".", "\\.")
|
||||||
|
.replace("*", ".*")
|
||||||
|
.replace("?", ".");
|
||||||
|
filenameRegex = Pattern.compile(regex, Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE);
|
||||||
|
}
|
||||||
|
searchRecursive(directory.toPath(), pattern.toLowerCase(), filenameRegex, recursive, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void searchRecursive(Path directory, String pattern, boolean recursive, 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, 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, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void searchRecursive(Path directory, String patternLower, Pattern filenameRegex, boolean recursive, 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, pattern, recursive, callback);
|
searchRecursive(entry, patternLower, filenameRegex, recursive, callback);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
String fileName = entry.getFileName().toString().toLowerCase();
|
String fileName = entry.getFileName().toString();
|
||||||
if (fileName.contains(pattern) || matchesPattern(fileName, pattern)) {
|
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;
|
||||||
|
}
|
||||||
|
if (matched) {
|
||||||
callback.onFileFound(entry.toFile());
|
callback.onFileFound(entry.toFile());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -178,16 +208,36 @@ public class FileOperations {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private static void searchContentsRecursive(Path directory, Pattern contentPattern, boolean recursive, SearchCallback callback) throws IOException {
|
||||||
* Check whether filename matches the pattern (supports * and ?)
|
try (DirectoryStream<Path> stream = Files.newDirectoryStream(directory)) {
|
||||||
*/
|
for (Path entry : stream) {
|
||||||
private static boolean matchesPattern(String fileName, String pattern) {
|
if (Files.isDirectory(entry)) {
|
||||||
String regex = pattern
|
if (recursive) {
|
||||||
.replace(".", "\\.")
|
searchContentsRecursive(entry, contentPattern, recursive, callback);
|
||||||
.replace("*", ".*")
|
|
||||||
.replace("?", ".");
|
|
||||||
return fileName.matches(regex);
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// 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(entry.toFile());
|
||||||
|
} catch (IOException ex) {
|
||||||
|
// Skip files that cannot be read as text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (AccessDeniedException e) {
|
||||||
|
// Ignore directories without access
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// legacy matchesPattern removed — filename wildcard handling is done via a precompiled Pattern
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Callback pro progress operací
|
* Callback pro progress operací
|
||||||
|
|||||||
@ -311,6 +311,23 @@ public class FileEditor extends JDialog {
|
|||||||
}
|
}
|
||||||
// Ensure byteTextOffsets length equals fileBytes length
|
// Ensure byteTextOffsets length equals fileBytes length
|
||||||
// If some bytes were missing due to empty file, leave as is
|
// If some bytes were missing due to empty file, leave as is
|
||||||
|
// After building the view text, position caret to the start of the rendered data
|
||||||
|
SwingUtilities.invokeLater(() -> {
|
||||||
|
try {
|
||||||
|
if (byteTextOffsets != null && !byteTextOffsets.isEmpty()) {
|
||||||
|
int pos = Math.max(0, byteTextOffsets.get(0));
|
||||||
|
textArea.setCaretPosition(pos);
|
||||||
|
// ensure the caret is visible at top-left
|
||||||
|
Rectangle vis = textArea.getVisibleRect();
|
||||||
|
Rectangle r = textArea.modelToView2D(pos).getBounds();
|
||||||
|
if (r != null) {
|
||||||
|
textArea.scrollRectToVisible(new Rectangle(r.x, r.y, vis.width, vis.height));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
textArea.setCaretPosition(0);
|
||||||
|
}
|
||||||
|
} catch (Exception ignore) {}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadHexPage() {
|
private void loadHexPage() {
|
||||||
@ -607,15 +624,12 @@ public class FileEditor extends JDialog {
|
|||||||
// makes the displayed offset follow scrolling (PgUp/PgDn or scrollbar)
|
// makes the displayed offset follow scrolling (PgUp/PgDn or scrollbar)
|
||||||
// rather than only caret movement.
|
// rather than only caret movement.
|
||||||
int refPos;
|
int refPos;
|
||||||
if (raf != null) {
|
|
||||||
try {
|
try {
|
||||||
Rectangle vis = textArea.getVisibleRect();
|
Rectangle vis = textArea.getVisibleRect();
|
||||||
Point topLeft = new Point(vis.x, vis.y);
|
Point topLeft = new Point(vis.x, vis.y);
|
||||||
refPos = textArea.viewToModel2D(topLeft);
|
refPos = textArea.viewToModel2D(topLeft);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
refPos = textArea.getCaretPosition();
|
// fallback to caret position if viewToModel fails
|
||||||
}
|
|
||||||
} else {
|
|
||||||
refPos = textArea.getCaretPosition();
|
refPos = textArea.getCaretPosition();
|
||||||
}
|
}
|
||||||
int caret = refPos;
|
int caret = refPos;
|
||||||
|
|||||||
@ -413,7 +413,7 @@ public class FilePanel extends JPanel {
|
|||||||
long free = drive.getUsableSpace();
|
long free = drive.getUsableSpace();
|
||||||
String freeGb = formatGbShort(free);
|
String freeGb = formatGbShort(free);
|
||||||
String totalGb = formatGbShort(total);
|
String totalGb = formatGbShort(total);
|
||||||
String info = String.format("%s %s free of %s GB", name, freeGb, totalGb);
|
String info = String.format("%s %s GB free of %s GB", name, freeGb, totalGb);
|
||||||
driveInfoLabel.setText(info);
|
driveInfoLabel.setText(info);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
driveInfoLabel.setText("");
|
driveInfoLabel.setText("");
|
||||||
|
|||||||
@ -862,6 +862,14 @@ public class FilePanelTab extends JPanel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Public wrapper to select an item by name from outside this class.
|
||||||
|
* Useful for other UI components to request focusing a specific file.
|
||||||
|
*/
|
||||||
|
public void selectItem(String name) {
|
||||||
|
selectItemByName(name);
|
||||||
|
}
|
||||||
|
|
||||||
public void toggleSelectionAndMoveDown() {
|
public void toggleSelectionAndMoveDown() {
|
||||||
int selectedRow = fileTable.getSelectedRow();
|
int selectedRow = fileTable.getSelectedRow();
|
||||||
if (selectedRow >= 0) {
|
if (selectedRow >= 0) {
|
||||||
|
|||||||
@ -453,9 +453,9 @@ public class MainWindow extends JFrame {
|
|||||||
KeyStroke.getKeyStroke(KeyEvent.VK_F2, InputEvent.ALT_DOWN_MASK),
|
KeyStroke.getKeyStroke(KeyEvent.VK_F2, InputEvent.ALT_DOWN_MASK),
|
||||||
JComponent.WHEN_IN_FOCUSED_WINDOW);
|
JComponent.WHEN_IN_FOCUSED_WINDOW);
|
||||||
|
|
||||||
// Ctrl+F - Search
|
// Alt+F7 - Search (changed from Ctrl+F)
|
||||||
rootPane.registerKeyboardAction(e -> showSearchDialog(),
|
rootPane.registerKeyboardAction(e -> showSearchDialog(),
|
||||||
KeyStroke.getKeyStroke(KeyEvent.VK_F, InputEvent.CTRL_DOWN_MASK),
|
KeyStroke.getKeyStroke(KeyEvent.VK_F7, InputEvent.ALT_DOWN_MASK),
|
||||||
JComponent.WHEN_IN_FOCUSED_WINDOW);
|
JComponent.WHEN_IN_FOCUSED_WINDOW);
|
||||||
|
|
||||||
// Ctrl+F1 - Full details
|
// Ctrl+F1 - Full details
|
||||||
@ -704,10 +704,56 @@ public class MainWindow extends JFrame {
|
|||||||
* Show search dialog
|
* Show search dialog
|
||||||
*/
|
*/
|
||||||
private void showSearchDialog() {
|
private void showSearchDialog() {
|
||||||
SearchDialog dialog = new SearchDialog(this, activePanel.getCurrentDirectory());
|
SearchDialog dialog = new SearchDialog(this, activePanel.getCurrentDirectory(), config);
|
||||||
dialog.setVisible(true);
|
dialog.setVisible(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the given file's parent directory in the panel that currently has focus
|
||||||
|
* and select the file in that panel.
|
||||||
|
*/
|
||||||
|
public void showFileInFocusedPanel(File file) {
|
||||||
|
if (file == null) return;
|
||||||
|
// Determine which panel currently has focus
|
||||||
|
java.awt.Component owner = java.awt.KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
|
||||||
|
FilePanel target = null;
|
||||||
|
if (owner != null) {
|
||||||
|
Component p = owner;
|
||||||
|
while (p != null) {
|
||||||
|
if (p == leftPanel) {
|
||||||
|
target = leftPanel;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (p == rightPanel) {
|
||||||
|
target = rightPanel;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
p = p.getParent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (target == null) target = activePanel != null ? activePanel : leftPanel;
|
||||||
|
|
||||||
|
final FilePanel chosen = target;
|
||||||
|
final File parentDir = file.getParentFile();
|
||||||
|
if (parentDir == null) return;
|
||||||
|
|
||||||
|
// Load directory and then select item by name on the EDT
|
||||||
|
SwingUtilities.invokeLater(() -> {
|
||||||
|
chosen.loadDirectory(parentDir);
|
||||||
|
// mark this panel active and refresh borders
|
||||||
|
activePanel = chosen;
|
||||||
|
updateActivePanelBorder();
|
||||||
|
// After loading, select the file name
|
||||||
|
SwingUtilities.invokeLater(() -> {
|
||||||
|
FilePanelTab tab = chosen.getCurrentTab();
|
||||||
|
if (tab != null) {
|
||||||
|
tab.selectItem(file.getName());
|
||||||
|
tab.getFileTable().requestFocusInWindow();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show file in internal viewer
|
* Show file in internal viewer
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -4,9 +4,12 @@ import com.kfmanager.model.FileItem;
|
|||||||
import com.kfmanager.service.FileOperations;
|
import com.kfmanager.service.FileOperations;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
|
import java.awt.event.KeyEvent;
|
||||||
|
import java.awt.event.InputEvent;
|
||||||
import javax.swing.table.AbstractTableModel;
|
import javax.swing.table.AbstractTableModel;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.awt.Desktop;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -15,22 +18,46 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
public class SearchDialog extends JDialog {
|
public class SearchDialog extends JDialog {
|
||||||
|
|
||||||
private JTextField patternField;
|
private JComboBox<String> patternCombo;
|
||||||
|
private JComboBox<String> contentPatternCombo;
|
||||||
private JCheckBox recursiveCheckBox;
|
private JCheckBox recursiveCheckBox;
|
||||||
|
private JCheckBox contentSearchCheckBox;
|
||||||
private JTable resultsTable;
|
private JTable resultsTable;
|
||||||
private ResultsTableModel tableModel;
|
private ResultsTableModel tableModel;
|
||||||
private JButton searchButton;
|
private JButton searchButton;
|
||||||
private JButton cancelButton;
|
private JButton cancelButton;
|
||||||
|
private JButton viewButton;
|
||||||
|
private JButton editButton;
|
||||||
|
private JProgressBar statusProgressBar;
|
||||||
|
private JLabel statusLabel;
|
||||||
|
private volatile int foundCount = 0;
|
||||||
private File searchDirectory;
|
private File searchDirectory;
|
||||||
private volatile boolean searching = false;
|
private volatile boolean searching = false;
|
||||||
|
private com.kfmanager.config.AppConfig config;
|
||||||
|
|
||||||
public SearchDialog(Frame parent, File searchDirectory) {
|
public SearchDialog(Frame parent, File searchDirectory, com.kfmanager.config.AppConfig config) {
|
||||||
super(parent, "Search files", true);
|
// Make the dialog modeless so it does not remain forced above other windows
|
||||||
|
super(parent, "Search files", false);
|
||||||
this.searchDirectory = searchDirectory;
|
this.searchDirectory = searchDirectory;
|
||||||
|
this.config = config;
|
||||||
initComponents();
|
initComponents();
|
||||||
|
// Allow the user to resize the search dialog
|
||||||
|
setResizable(true);
|
||||||
|
// sensible minimum size so layout remains usable
|
||||||
|
setMinimumSize(new Dimension(480, 320));
|
||||||
|
// Restore previous bounds if config present
|
||||||
|
if (this.config != null) {
|
||||||
|
try {
|
||||||
|
this.config.restoreSearchDialogState(this);
|
||||||
|
} catch (Exception ignore) {
|
||||||
setSize(700, 500);
|
setSize(700, 500);
|
||||||
setLocationRelativeTo(parent);
|
setLocationRelativeTo(parent);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
setSize(700, 500);
|
||||||
|
setLocationRelativeTo(parent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void initComponents() {
|
private void initComponents() {
|
||||||
setLayout(new BorderLayout(10, 10));
|
setLayout(new BorderLayout(10, 10));
|
||||||
@ -48,9 +75,19 @@ public class SearchDialog extends JDialog {
|
|||||||
|
|
||||||
gbc.gridx = 1;
|
gbc.gridx = 1;
|
||||||
gbc.weightx = 1.0;
|
gbc.weightx = 1.0;
|
||||||
patternField = new JTextField();
|
// Pattern input is an editable combo box populated from history
|
||||||
patternField.setToolTipText("Enter filename or pattern (* for any chars, ? for single char)");
|
java.util.List<String> history = config != null ? config.getSearchHistory() : java.util.Collections.emptyList();
|
||||||
searchPanel.add(patternField, gbc);
|
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.gridx = 0;
|
gbc.gridx = 0;
|
||||||
gbc.gridy = 1;
|
gbc.gridy = 1;
|
||||||
@ -58,7 +95,30 @@ public class SearchDialog extends JDialog {
|
|||||||
recursiveCheckBox = new JCheckBox("Include subdirectories", true);
|
recursiveCheckBox = new JCheckBox("Include subdirectories", true);
|
||||||
searchPanel.add(recursiveCheckBox, gbc);
|
searchPanel.add(recursiveCheckBox, gbc);
|
||||||
|
|
||||||
|
// Row for content text pattern
|
||||||
|
gbc.gridx = 0;
|
||||||
gbc.gridy = 2;
|
gbc.gridy = 2;
|
||||||
|
gbc.gridwidth = 1;
|
||||||
|
searchPanel.add(new JLabel("Text:"), 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.gridx = 0;
|
||||||
|
gbc.gridy = 3;
|
||||||
|
gbc.gridwidth = 2;
|
||||||
|
contentSearchCheckBox = new JCheckBox("Search inside file contents", false);
|
||||||
|
searchPanel.add(contentSearchCheckBox, gbc);
|
||||||
|
|
||||||
|
gbc.gridy = 4;
|
||||||
JLabel pathLabel = new JLabel("Directory: " + searchDirectory.getAbsolutePath());
|
JLabel pathLabel = new JLabel("Directory: " + searchDirectory.getAbsolutePath());
|
||||||
pathLabel.setFont(pathLabel.getFont().deriveFont(Font.ITALIC));
|
pathLabel.setFont(pathLabel.getFont().deriveFont(Font.ITALIC));
|
||||||
searchPanel.add(pathLabel, gbc);
|
searchPanel.add(pathLabel, gbc);
|
||||||
@ -70,6 +130,8 @@ public class SearchDialog extends JDialog {
|
|||||||
resultsTable = new JTable(tableModel);
|
resultsTable = new JTable(tableModel);
|
||||||
resultsTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
resultsTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||||
resultsTable.setFont(new Font("Monospaced", Font.PLAIN, 12));
|
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(0).setPreferredWidth(400);
|
||||||
resultsTable.getColumnModel().getColumn(1).setPreferredWidth(100);
|
resultsTable.getColumnModel().getColumn(1).setPreferredWidth(100);
|
||||||
@ -85,16 +147,46 @@ public class SearchDialog extends JDialog {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Enable/disable view/edit buttons depending on selection
|
||||||
|
resultsTable.getSelectionModel().addListSelectionListener(e -> {
|
||||||
|
int sel = resultsTable.getSelectedRow();
|
||||||
|
boolean ok = false;
|
||||||
|
if (sel >= 0) {
|
||||||
|
FileItem it = tableModel.getResult(sel);
|
||||||
|
ok = it != null && !it.isDirectory() && !"..".equals(it.getName());
|
||||||
|
}
|
||||||
|
viewButton.setEnabled(ok);
|
||||||
|
editButton.setEnabled(ok);
|
||||||
|
});
|
||||||
|
|
||||||
JScrollPane scrollPane = new JScrollPane(resultsTable);
|
JScrollPane scrollPane = new JScrollPane(resultsTable);
|
||||||
scrollPane.setBorder(BorderFactory.createTitledBorder("Výsledky"));
|
scrollPane.setBorder(BorderFactory.createTitledBorder("Výsledky"));
|
||||||
add(scrollPane, BorderLayout.CENTER);
|
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 s tlačítky
|
// Panel s tlačítky
|
||||||
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
|
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
|
||||||
|
|
||||||
searchButton = new JButton("Hledat");
|
searchButton = new JButton("Hledat");
|
||||||
searchButton.addActionListener(e -> performSearch());
|
searchButton.addActionListener(e -> performSearch());
|
||||||
|
|
||||||
|
viewButton = new JButton("Zobrazit");
|
||||||
|
viewButton.setEnabled(false);
|
||||||
|
viewButton.addActionListener(e -> viewSelectedFile());
|
||||||
|
|
||||||
|
editButton = new JButton("Upravit");
|
||||||
|
editButton.setEnabled(false);
|
||||||
|
editButton.addActionListener(e -> editSelectedFile());
|
||||||
|
|
||||||
cancelButton = new JButton("Zavřít");
|
cancelButton = new JButton("Zavřít");
|
||||||
cancelButton.addActionListener(e -> dispose());
|
cancelButton.addActionListener(e -> dispose());
|
||||||
|
|
||||||
@ -102,43 +194,267 @@ public class SearchDialog extends JDialog {
|
|||||||
openButton.addActionListener(e -> openSelectedFile());
|
openButton.addActionListener(e -> openSelectedFile());
|
||||||
|
|
||||||
buttonPanel.add(searchButton);
|
buttonPanel.add(searchButton);
|
||||||
|
buttonPanel.add(viewButton);
|
||||||
|
buttonPanel.add(editButton);
|
||||||
buttonPanel.add(openButton);
|
buttonPanel.add(openButton);
|
||||||
buttonPanel.add(cancelButton);
|
buttonPanel.add(cancelButton);
|
||||||
|
|
||||||
add(buttonPanel, BorderLayout.SOUTH);
|
// 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);
|
||||||
|
|
||||||
// Enter pro spuštění hledání
|
// Require explicit Enter to start search when choosing from history.
|
||||||
patternField.addActionListener(e -> performSearch());
|
// Bind Enter on the combo editor component so selecting an item from the
|
||||||
|
// popup does not automatically trigger search until user confirms.
|
||||||
|
java.awt.Component editorComp = patternCombo.getEditor().getEditorComponent();
|
||||||
|
if (editorComp instanceof javax.swing.text.JTextComponent) {
|
||||||
|
javax.swing.text.JTextComponent tc = (javax.swing.text.JTextComponent) editorComp;
|
||||||
|
tc.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "confirmSearch");
|
||||||
|
tc.getActionMap().put("confirmSearch", new AbstractAction() {
|
||||||
|
@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 (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
|
||||||
|
performSearch();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Same explicit-Enter behavior for content text pattern combo
|
||||||
|
try {
|
||||||
|
java.awt.Component contentEditorComp = contentPatternCombo.getEditor().getEditorComponent();
|
||||||
|
if (contentEditorComp instanceof javax.swing.text.JTextComponent) {
|
||||||
|
javax.swing.text.JTextComponent tc2 = (javax.swing.text.JTextComponent) contentEditorComp;
|
||||||
|
tc2.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "confirmSearchContent");
|
||||||
|
tc2.getActionMap().put("confirmSearchContent", new AbstractAction() {
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(java.awt.event.ActionEvent e) {
|
||||||
|
try {
|
||||||
|
if (contentPatternCombo.isPopupVisible()) {
|
||||||
|
Object sel = contentPatternCombo.getSelectedItem();
|
||||||
|
if (sel != null) contentPatternCombo.getEditor().setItem(sel.toString());
|
||||||
|
contentPatternCombo.hidePopup();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (Exception ignore) {}
|
||||||
|
performSearch();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (Exception ignore) {}
|
||||||
|
|
||||||
|
// Alt+F focuses filename (pattern) input; Alt+T focuses content text input
|
||||||
|
getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
|
||||||
|
KeyStroke.getKeyStroke(KeyEvent.VK_F, InputEvent.ALT_DOWN_MASK), "focusFilename");
|
||||||
|
getRootPane().getActionMap().put("focusFilename", new AbstractAction() {
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(java.awt.event.ActionEvent e) {
|
||||||
|
try {
|
||||||
|
java.awt.Component ed = patternCombo.getEditor().getEditorComponent();
|
||||||
|
if (ed != null) {
|
||||||
|
ed.requestFocusInWindow();
|
||||||
|
if (ed instanceof javax.swing.text.JTextComponent) {
|
||||||
|
((javax.swing.text.JTextComponent) ed).selectAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception ignore) {}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
|
||||||
|
KeyStroke.getKeyStroke(KeyEvent.VK_T, InputEvent.ALT_DOWN_MASK), "focusContentText");
|
||||||
|
getRootPane().getActionMap().put("focusContentText", new AbstractAction() {
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(java.awt.event.ActionEvent e) {
|
||||||
|
try {
|
||||||
|
// Toggle the content-search checkbox (on/off)
|
||||||
|
try {
|
||||||
|
if (contentSearchCheckBox != null) {
|
||||||
|
contentSearchCheckBox.setSelected(!contentSearchCheckBox.isSelected());
|
||||||
|
}
|
||||||
|
} catch (Exception ignore) {}
|
||||||
|
|
||||||
|
java.awt.Component ed = contentPatternCombo.getEditor().getEditorComponent();
|
||||||
|
if (ed != null) {
|
||||||
|
ed.requestFocusInWindow();
|
||||||
|
if (ed instanceof javax.swing.text.JTextComponent) {
|
||||||
|
((javax.swing.text.JTextComponent) ed).selectAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception ignore) {}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Escape closes the dialog (and cancels ongoing search)
|
||||||
|
getRootPane().registerKeyboardAction(e -> {
|
||||||
|
searching = false;
|
||||||
|
dispose();
|
||||||
|
}, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_IN_FOCUSED_WINDOW);
|
||||||
|
|
||||||
|
// F3 = view, F4 = edit when dialog is active
|
||||||
|
getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
|
||||||
|
KeyStroke.getKeyStroke(KeyEvent.VK_F3, 0), "viewResult");
|
||||||
|
getRootPane().getActionMap().put("viewResult", new AbstractAction() {
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(java.awt.event.ActionEvent e) {
|
||||||
|
viewSelectedFile();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
|
||||||
|
KeyStroke.getKeyStroke(KeyEvent.VK_F4, 0), "editResult");
|
||||||
|
getRootPane().getActionMap().put("editResult", new AbstractAction() {
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(java.awt.event.ActionEvent e) {
|
||||||
|
editSelectedFile();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void dispose() {
|
||||||
|
// Persist bounds before disposing
|
||||||
|
try {
|
||||||
|
if (config != null) {
|
||||||
|
// also persist current pattern into history
|
||||||
|
try {
|
||||||
|
Object it = patternCombo != null ? patternCombo.getEditor().getItem() : null;
|
||||||
|
String cur = it != null ? it.toString().trim() : null;
|
||||||
|
if (cur != null && !cur.isEmpty()) {
|
||||||
|
java.util.List<String> hist = new java.util.ArrayList<>(config.getSearchHistory());
|
||||||
|
hist.remove(cur);
|
||||||
|
hist.add(0, cur);
|
||||||
|
int max = 20;
|
||||||
|
while (hist.size() > max) hist.remove(hist.size() - 1);
|
||||||
|
config.saveSearchHistory(hist);
|
||||||
|
}
|
||||||
|
} catch (Exception ignore) {}
|
||||||
|
// also persist current content search pattern into history
|
||||||
|
try {
|
||||||
|
Object cit = contentPatternCombo != null ? contentPatternCombo.getEditor().getItem() : null;
|
||||||
|
String ccur = cit != null ? cit.toString().trim() : null;
|
||||||
|
if (ccur != null && !ccur.isEmpty()) {
|
||||||
|
java.util.List<String> chist = new java.util.ArrayList<>(config.getContentSearchHistory());
|
||||||
|
chist.remove(ccur);
|
||||||
|
chist.add(0, ccur);
|
||||||
|
int maxc = 20;
|
||||||
|
while (chist.size() > maxc) chist.remove(chist.size() - 1);
|
||||||
|
config.saveContentSearchHistory(chist);
|
||||||
|
}
|
||||||
|
} catch (Exception ignore) {}
|
||||||
|
config.saveSearchDialogState(this);
|
||||||
|
config.saveConfig();
|
||||||
|
}
|
||||||
|
} catch (Exception ignore) {}
|
||||||
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provede vyhledávání
|
* Provede vyhledávání
|
||||||
*/
|
*/
|
||||||
private void performSearch() {
|
private void performSearch() {
|
||||||
String pattern = patternField.getText().trim();
|
String namePat = "";
|
||||||
if (pattern.isEmpty()) {
|
try {
|
||||||
|
Object it = patternCombo.getEditor().getItem();
|
||||||
|
namePat = it != null ? it.toString().trim() : "";
|
||||||
|
} catch (Exception ex) { namePat = ""; }
|
||||||
|
|
||||||
|
String contentPat = "";
|
||||||
|
try {
|
||||||
|
Object cit = contentPatternCombo.getEditor().getItem();
|
||||||
|
contentPat = cit != null ? cit.toString().trim() : "";
|
||||||
|
} catch (Exception ex) { contentPat = ""; }
|
||||||
|
|
||||||
|
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,
|
JOptionPane.showMessageDialog(this,
|
||||||
"Zadejte hledaný vzor",
|
"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)
|
||||||
|
if (config != null) {
|
||||||
|
try {
|
||||||
|
if (isContentSearch) {
|
||||||
|
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());
|
||||||
|
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<String> m = (javax.swing.DefaultComboBoxModel<String>) patternCombo.getModel();
|
||||||
|
m.removeAllElements();
|
||||||
|
for (String s : hist) m.addElement(s);
|
||||||
|
patternCombo.setSelectedItem(namePat);
|
||||||
|
}
|
||||||
|
} catch (Exception ignore) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
final String pattern = isContentSearch ? contentPat : namePat;
|
||||||
|
|
||||||
|
// Reset and show status
|
||||||
|
foundCount = 0;
|
||||||
|
statusLabel.setText("Searching...");
|
||||||
|
statusProgressBar.setVisible(true);
|
||||||
|
statusProgressBar.setIndeterminate(true);
|
||||||
|
|
||||||
// Spustit vyhledávání v samostatném vlákně
|
// Spustit vyhledávání v samostatném vlákně
|
||||||
SwingWorker<Void, File> worker = new SwingWorker<Void, File>() {
|
SwingWorker<Void, File> worker = new SwingWorker<Void, File>() {
|
||||||
@Override
|
@Override
|
||||||
protected Void doInBackground() throws Exception {
|
protected Void doInBackground() throws Exception {
|
||||||
FileOperations.search(searchDirectory, pattern, recursiveCheckBox.isSelected(),
|
if (isContentSearch) {
|
||||||
file -> {
|
FileOperations.searchContents(searchDirectory, pattern, recursiveCheckBox.isSelected(), file -> {
|
||||||
if (!searching) {
|
if (!searching) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
publish(file);
|
publish(file);
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
FileOperations.search(searchDirectory, pattern, recursiveCheckBox.isSelected(), file -> {
|
||||||
|
if (!searching) return;
|
||||||
|
publish(file);
|
||||||
|
});
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,6 +462,16 @@ public class SearchDialog extends JDialog {
|
|||||||
protected void process(List<File> chunks) {
|
protected void process(List<File> chunks) {
|
||||||
for (File file : chunks) {
|
for (File file : chunks) {
|
||||||
tableModel.addResult(new FileItem(file));
|
tableModel.addResult(new FileItem(file));
|
||||||
|
// update found count and status
|
||||||
|
foundCount++;
|
||||||
|
statusLabel.setText("Found " + foundCount + " — searching...");
|
||||||
|
// If this is the first found file, select it and focus the table
|
||||||
|
if (foundCount == 1) {
|
||||||
|
try {
|
||||||
|
resultsTable.setRowSelectionInterval(0, 0);
|
||||||
|
resultsTable.requestFocusInWindow();
|
||||||
|
} catch (Exception ignore) {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,8 +479,12 @@ public class SearchDialog extends JDialog {
|
|||||||
protected void done() {
|
protected void done() {
|
||||||
searchButton.setEnabled(true);
|
searchButton.setEnabled(true);
|
||||||
searching = false;
|
searching = false;
|
||||||
|
// finalize status
|
||||||
|
statusProgressBar.setIndeterminate(false);
|
||||||
|
statusProgressBar.setVisible(false);
|
||||||
try {
|
try {
|
||||||
get();
|
get();
|
||||||
|
statusLabel.setText("Done — found " + foundCount + " files");
|
||||||
if (tableModel.getRowCount() == 0) {
|
if (tableModel.getRowCount() == 0) {
|
||||||
JOptionPane.showMessageDialog(SearchDialog.this,
|
JOptionPane.showMessageDialog(SearchDialog.this,
|
||||||
"Nebyly nalezeny žádné soubory",
|
"Nebyly nalezeny žádné soubory",
|
||||||
@ -162,6 +492,7 @@ public class SearchDialog extends JDialog {
|
|||||||
JOptionPane.INFORMATION_MESSAGE);
|
JOptionPane.INFORMATION_MESSAGE);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
statusLabel.setText("Error");
|
||||||
JOptionPane.showMessageDialog(SearchDialog.this,
|
JOptionPane.showMessageDialog(SearchDialog.this,
|
||||||
"Chyba při hledání: " + e.getMessage(),
|
"Chyba při hledání: " + e.getMessage(),
|
||||||
"Chyba",
|
"Chyba",
|
||||||
@ -181,6 +512,15 @@ public class SearchDialog extends JDialog {
|
|||||||
if (selectedRow >= 0) {
|
if (selectedRow >= 0) {
|
||||||
FileItem item = tableModel.getResult(selectedRow);
|
FileItem item = tableModel.getResult(selectedRow);
|
||||||
try {
|
try {
|
||||||
|
// Open location inside the application: show parent directory in the focused panel and select the file
|
||||||
|
java.awt.Window w = SwingUtilities.getWindowAncestor(this);
|
||||||
|
if (w instanceof MainWindow) {
|
||||||
|
MainWindow mw = (MainWindow) w;
|
||||||
|
mw.showFileInFocusedPanel(item.getFile());
|
||||||
|
dispose();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Fallback to opening in system explorer
|
||||||
Desktop.getDesktop().open(item.getFile().getParentFile());
|
Desktop.getDesktop().open(item.getFile().getParentFile());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
JOptionPane.showMessageDialog(this,
|
JOptionPane.showMessageDialog(this,
|
||||||
@ -191,6 +531,44 @@ public class SearchDialog extends JDialog {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open the selected file in the internal viewer (read-only)
|
||||||
|
*/
|
||||||
|
private void viewSelectedFile() {
|
||||||
|
int sel = resultsTable.getSelectedRow();
|
||||||
|
if (sel >= 0) {
|
||||||
|
FileItem item = tableModel.getResult(sel);
|
||||||
|
if (item != null && !item.isDirectory() && !"..".equals(item.getName())) {
|
||||||
|
try {
|
||||||
|
Frame owner = (Frame) SwingUtilities.getWindowAncestor(this);
|
||||||
|
FileEditor viewer = new FileEditor(owner, item.getFile(), config, true);
|
||||||
|
viewer.setVisible(true);
|
||||||
|
} catch (Exception e) {
|
||||||
|
JOptionPane.showMessageDialog(this, "Chyba při otevírání souboru: " + e.getMessage(), "Chyba", JOptionPane.ERROR_MESSAGE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open the selected file in the internal editor (editable)
|
||||||
|
*/
|
||||||
|
private void editSelectedFile() {
|
||||||
|
int sel = resultsTable.getSelectedRow();
|
||||||
|
if (sel >= 0) {
|
||||||
|
FileItem item = tableModel.getResult(sel);
|
||||||
|
if (item != null && !item.isDirectory() && !"..".equals(item.getName())) {
|
||||||
|
try {
|
||||||
|
Frame owner = (Frame) SwingUtilities.getWindowAncestor(this);
|
||||||
|
FileEditor editor = new FileEditor(owner, item.getFile(), config, false);
|
||||||
|
editor.setVisible(true);
|
||||||
|
} catch (Exception e) {
|
||||||
|
JOptionPane.showMessageDialog(this, "Chyba při otevírání souboru: " + e.getMessage(), "Chyba", JOptionPane.ERROR_MESSAGE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Model tabulky pro výsledky hledání
|
* Model tabulky pro výsledky hledání
|
||||||
*/
|
*/
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user