added dir synchronization feature, complete translation to english
This commit is contained in:
parent
61b6269a5e
commit
413487f25c
44
README.md
44
README.md
@ -1,47 +1,51 @@
|
||||
# KF File Manager
|
||||
|
||||
Dvoupanelový souborový manažer podobný Total Commander, vytvořený v Java 11.
|
||||
A two-panel file manager similar to Total Commander, built with Java 11.
|
||||
|
||||
## Funkce
|
||||
## Features
|
||||
|
||||
- **Two panels** for browsing files and directories
|
||||
- **Copying** files and directories (F5)
|
||||
- **Moving** files and directories (F6)
|
||||
- **Rename** (Shift+F6)
|
||||
- **Create directory** (F7)
|
||||
- **Delete** files and directories (F8)
|
||||
- **Přejmenování** (Shift+F6)
|
||||
- **Vyhledávání** souborů (Ctrl+F)
|
||||
- **Přepínání** mezi panely (TAB)
|
||||
- **Search** for files (Ctrl+F)
|
||||
- **Sync Directories** - side-by-side folder comparison and synchronization
|
||||
- **Toggle** between panels (TAB)
|
||||
- **Navigation** - double-click or Enter to open a directory
|
||||
- **Zobrazení** velikosti souborů, data modifikace
|
||||
- **View** file sizes, modification dates, and attributes
|
||||
|
||||
## Spuštění
|
||||
## Running
|
||||
|
||||
```bash
|
||||
mvn clean compile
|
||||
mvn exec:java -Dexec.mainClass="cz.kamma.kfmanager.MainApp"
|
||||
```
|
||||
|
||||
Nebo vytvoření JAR souboru:
|
||||
Or build a JAR file:
|
||||
|
||||
```bash
|
||||
mvn clean package
|
||||
java -jar target/kf-manager-1.0-SNAPSHOT.jar
|
||||
```
|
||||
|
||||
## Klávesové zkratky
|
||||
## Keyboard Shortcuts
|
||||
|
||||
- **F5** - Kopírovat
|
||||
- **F6** - Přesunout
|
||||
- **Shift+F6** - Přejmenovat
|
||||
- **F7** - Nový adresář
|
||||
- **F8** - Smazat
|
||||
- **TAB** - Přepnout mezi panely
|
||||
- **Ctrl+F** - Vyhledat soubory
|
||||
- **Enter** - Otevřít adresář
|
||||
- **Backspace** - Nadřazený adresář
|
||||
- **F3** - View file (internal viewer)
|
||||
- **F4** - Edit file
|
||||
- **F5** - Copy
|
||||
- **F6** - Move
|
||||
- **Shift+F6** - Rename
|
||||
- **F7** - New directory
|
||||
- **F8** - Delete
|
||||
- **TAB** - Toggle between panels
|
||||
- **Ctrl+F** - Search files
|
||||
- **Ctrl+Y** - Sync directories
|
||||
- **Enter** - Open directory
|
||||
- **Backspace** - Parent directory
|
||||
|
||||
## Požadavky
|
||||
## Requirements
|
||||
|
||||
- Java 11 nebo vyšší
|
||||
- Java 11 or higher
|
||||
- Maven 3.6+
|
||||
|
||||
@ -7,5 +7,5 @@ Exec=java -jar /home/kamma/projects/kf-manager/target/kf-manager-1.0-SNAPSHOT-ja
|
||||
Name=KF File Manager
|
||||
Icon=/home/kamma/projects/kf-manager/src/main/resources/icon.png
|
||||
StartupWMClass=cz-kamma-kfmanager-MainApp
|
||||
Comment=Dvoupanelový souborový manažer pro Linux
|
||||
Comment=Two-panel file manager for Linux
|
||||
Categories=Utility;System;
|
||||
|
||||
@ -24,7 +24,7 @@ public class DriveSelector extends JDialog {
|
||||
setLayout(new BorderLayout(10, 10));
|
||||
((JComponent) getContentPane()).setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
|
||||
|
||||
// Získat seznam dostupných disků
|
||||
// Get list of available drives
|
||||
File[] roots = File.listRoots();
|
||||
List<DriveInfo> drives = new ArrayList<>();
|
||||
|
||||
@ -32,7 +32,7 @@ public class DriveSelector extends JDialog {
|
||||
drives.add(new DriveInfo(root));
|
||||
}
|
||||
|
||||
// Seznam disků
|
||||
// List of drives
|
||||
JList<DriveInfo> driveList = new JList<>(drives.toArray(new DriveInfo[0]));
|
||||
driveList.setFont(new Font("Monospaced", Font.PLAIN, 14));
|
||||
driveList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
@ -53,7 +53,7 @@ public class DriveSelector extends JDialog {
|
||||
}
|
||||
});
|
||||
|
||||
// Double-click pro výběr
|
||||
// Double-click to select
|
||||
driveList.addMouseListener(new java.awt.event.MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(java.awt.event.MouseEvent e) {
|
||||
@ -67,7 +67,7 @@ public class DriveSelector extends JDialog {
|
||||
}
|
||||
});
|
||||
|
||||
// Enter pro výběr
|
||||
// Enter to select
|
||||
driveList.addKeyListener(new java.awt.event.KeyAdapter() {
|
||||
@Override
|
||||
public void keyPressed(java.awt.event.KeyEvent e) {
|
||||
@ -84,7 +84,7 @@ public class DriveSelector extends JDialog {
|
||||
JScrollPane scrollPane = new JScrollPane(driveList);
|
||||
add(scrollPane, BorderLayout.CENTER);
|
||||
|
||||
// Panel s tlačítky
|
||||
// Panel with buttons
|
||||
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
|
||||
|
||||
JButton okButton = new JButton("OK");
|
||||
@ -109,7 +109,7 @@ public class DriveSelector extends JDialog {
|
||||
|
||||
add(buttonPanel, BorderLayout.SOUTH);
|
||||
|
||||
// Automaticky vybrat první disk
|
||||
// Automatically select the first drive
|
||||
if (drives.size() > 0) {
|
||||
driveList.setSelectedIndex(0);
|
||||
}
|
||||
@ -122,7 +122,7 @@ public class DriveSelector extends JDialog {
|
||||
}
|
||||
|
||||
/**
|
||||
* Pomocná třída pro informace o disku
|
||||
* Helper class for drive information
|
||||
*/
|
||||
private static class DriveInfo {
|
||||
private final File root;
|
||||
|
||||
@ -61,7 +61,7 @@ public class FileEditor extends JDialog {
|
||||
}
|
||||
|
||||
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);
|
||||
super(parent, (readOnly ? "Viewer - " : "Editor - ") + (virtualPath != null ? virtualPath.substring(virtualPath.lastIndexOf(File.separator) + 1) : file.getName()), ModalityType.MODELESS);
|
||||
this.file = file;
|
||||
this.virtualPath = virtualPath;
|
||||
this.config = config;
|
||||
@ -101,10 +101,10 @@ public class FileEditor extends JDialog {
|
||||
searchField = new JTextField(20);
|
||||
searchField.addActionListener(e -> findNext());
|
||||
|
||||
JButton nextBtn = new JButton("Dále");
|
||||
JButton nextBtn = new JButton("Next");
|
||||
nextBtn.addActionListener(e -> findNext());
|
||||
|
||||
JButton prevBtn = new JButton("Zpět");
|
||||
JButton prevBtn = new JButton("Back");
|
||||
prevBtn.addActionListener(e -> findPrevious());
|
||||
|
||||
JButton closeBtn = new JButton("X");
|
||||
@ -113,7 +113,7 @@ public class FileEditor extends JDialog {
|
||||
|
||||
searchStatusLabel = new JLabel("");
|
||||
|
||||
searchPanel.add(new JLabel("Hledat:"));
|
||||
searchPanel.add(new JLabel("Search:"));
|
||||
searchPanel.add(searchField);
|
||||
searchPanel.add(nextBtn);
|
||||
searchPanel.add(prevBtn);
|
||||
@ -199,7 +199,7 @@ public class FileEditor extends JDialog {
|
||||
} catch (Exception ignore) {}
|
||||
searchStatusLabel.setText("");
|
||||
} else {
|
||||
searchStatusLabel.setText("Nenalezeno");
|
||||
searchStatusLabel.setText("Not found");
|
||||
}
|
||||
}
|
||||
|
||||
@ -232,7 +232,7 @@ public class FileEditor extends JDialog {
|
||||
} catch (Exception ignore) {}
|
||||
searchStatusLabel.setText("");
|
||||
} else {
|
||||
searchStatusLabel.setText("Nenalezeno");
|
||||
searchStatusLabel.setText("Not found");
|
||||
}
|
||||
}
|
||||
|
||||
@ -259,7 +259,7 @@ public class FileEditor extends JDialog {
|
||||
// Menu bar
|
||||
createMenuBar();
|
||||
|
||||
// Textová oblast (editable nebo read-only)
|
||||
// Text area (editable or read-only)
|
||||
textArea = new JTextArea();
|
||||
textArea.setFont(config.getEditorFont());
|
||||
textArea.setTabSize(4);
|
||||
@ -271,7 +271,7 @@ public class FileEditor extends JDialog {
|
||||
undoManager.addEdit(e.getEdit());
|
||||
});
|
||||
|
||||
// Sledování změn (pouze pro editovatelný režim)
|
||||
// Track changes (editable mode only)
|
||||
if (!readOnly) {
|
||||
textArea.getDocument().addDocumentListener(new javax.swing.event.DocumentListener() {
|
||||
public void insertUpdate(javax.swing.event.DocumentEvent e) { setModified(true); }
|
||||
@ -295,10 +295,10 @@ public class FileEditor extends JDialog {
|
||||
// Apply appearance settings (colors, contrast caret etc)
|
||||
applyAppearance();
|
||||
|
||||
// Kontextové menu pro clipboard
|
||||
// Context menu for clipboard
|
||||
setupContextMenu();
|
||||
|
||||
// Klávesové zkratky
|
||||
// Keyboard shortcuts
|
||||
setupKeyBindings();
|
||||
|
||||
// Caret listener to update position and selection info
|
||||
@ -314,11 +314,11 @@ public class FileEditor extends JDialog {
|
||||
private void setupContextMenu() {
|
||||
JPopupMenu popup = new JPopupMenu();
|
||||
|
||||
JMenuItem undoItem = new JMenuItem("Zpět");
|
||||
JMenuItem undoItem = new JMenuItem("Undo");
|
||||
undoItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Z, InputEvent.CTRL_DOWN_MASK));
|
||||
undoItem.addActionListener(e -> { if (undoManager.canUndo()) undoManager.undo(); });
|
||||
|
||||
JMenuItem redoItem = new JMenuItem("Znovu");
|
||||
JMenuItem redoItem = new JMenuItem("Redo");
|
||||
redoItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Y, InputEvent.CTRL_DOWN_MASK));
|
||||
redoItem.addActionListener(e -> { if (undoManager.canRedo()) undoManager.redo(); });
|
||||
|
||||
@ -327,15 +327,15 @@ public class FileEditor extends JDialog {
|
||||
popup.addSeparator();
|
||||
|
||||
JMenuItem cutItem = new JMenuItem(new javax.swing.text.DefaultEditorKit.CutAction());
|
||||
cutItem.setText("Vyjmout");
|
||||
cutItem.setText("Cut");
|
||||
cutItem.setMnemonic(KeyEvent.VK_X);
|
||||
|
||||
JMenuItem copyItem = new JMenuItem(new javax.swing.text.DefaultEditorKit.CopyAction());
|
||||
copyItem.setText("Kopírovat");
|
||||
copyItem.setText("Copy");
|
||||
copyItem.setMnemonic(KeyEvent.VK_C);
|
||||
|
||||
JMenuItem pasteItem = new JMenuItem(new javax.swing.text.DefaultEditorKit.PasteAction());
|
||||
pasteItem.setText("Vložit");
|
||||
pasteItem.setText("Paste");
|
||||
pasteItem.setMnemonic(KeyEvent.VK_V);
|
||||
|
||||
popup.add(cutItem);
|
||||
@ -343,7 +343,7 @@ public class FileEditor extends JDialog {
|
||||
popup.add(pasteItem);
|
||||
popup.addSeparator();
|
||||
|
||||
JMenuItem selectAllItem = new JMenuItem("Vybrat vše");
|
||||
JMenuItem selectAllItem = new JMenuItem("Select All");
|
||||
selectAllItem.setMnemonic(KeyEvent.VK_A);
|
||||
selectAllItem.addActionListener(e -> textArea.selectAll());
|
||||
popup.add(selectAllItem);
|
||||
@ -428,9 +428,9 @@ public class FileEditor extends JDialog {
|
||||
JMenu fileMenu = new JMenu("File");
|
||||
fileMenu.setMnemonic(java.awt.event.KeyEvent.VK_S);
|
||||
|
||||
// Uložit - pouze v editovacím režimu
|
||||
// Save - editable mode only
|
||||
if (!readOnly) {
|
||||
JMenuItem saveItem = new JMenuItem("Uložit");
|
||||
JMenuItem saveItem = new JMenuItem("Save");
|
||||
saveItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F2, 0));
|
||||
saveItem.addActionListener(e -> saveFile());
|
||||
fileMenu.add(saveItem);
|
||||
@ -438,23 +438,23 @@ public class FileEditor extends JDialog {
|
||||
fileMenu.addSeparator();
|
||||
}
|
||||
|
||||
JMenuItem closeItem = new JMenuItem("Zavřít");
|
||||
JMenuItem closeItem = new JMenuItem("Close");
|
||||
closeItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0));
|
||||
closeItem.addActionListener(e -> closeEditor());
|
||||
fileMenu.add(closeItem);
|
||||
|
||||
menuBar.add(fileMenu);
|
||||
|
||||
// Menu Edit
|
||||
// Edit Menu
|
||||
JMenu editMenu = new JMenu("Edit");
|
||||
editMenu.setMnemonic(java.awt.event.KeyEvent.VK_E);
|
||||
|
||||
JMenuItem undoItem = new JMenuItem("Zpět");
|
||||
JMenuItem undoItem = new JMenuItem("Undo");
|
||||
undoItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Z, InputEvent.CTRL_DOWN_MASK));
|
||||
undoItem.addActionListener(e -> { if (undoManager.canUndo()) undoManager.undo(); });
|
||||
editMenu.add(undoItem);
|
||||
|
||||
JMenuItem redoItem = new JMenuItem("Znovu");
|
||||
JMenuItem redoItem = new JMenuItem("Redo");
|
||||
redoItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Y, InputEvent.CTRL_DOWN_MASK));
|
||||
redoItem.addActionListener(e -> { if (undoManager.canRedo()) undoManager.redo(); });
|
||||
editMenu.add(redoItem);
|
||||
@ -462,35 +462,35 @@ public class FileEditor extends JDialog {
|
||||
editMenu.addSeparator();
|
||||
|
||||
JMenuItem cutItem = new JMenuItem(new javax.swing.text.DefaultEditorKit.CutAction());
|
||||
cutItem.setText("Vyjmout");
|
||||
cutItem.setText("Cut");
|
||||
cutItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_X, InputEvent.CTRL_DOWN_MASK));
|
||||
editMenu.add(cutItem);
|
||||
|
||||
JMenuItem copyItem = new JMenuItem(new javax.swing.text.DefaultEditorKit.CopyAction());
|
||||
copyItem.setText("Kopírovat");
|
||||
copyItem.setText("Copy");
|
||||
copyItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C, InputEvent.CTRL_DOWN_MASK));
|
||||
editMenu.add(copyItem);
|
||||
|
||||
JMenuItem pasteItem = new JMenuItem(new javax.swing.text.DefaultEditorKit.PasteAction());
|
||||
pasteItem.setText("Vložit");
|
||||
pasteItem.setText("Paste");
|
||||
pasteItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_V, InputEvent.CTRL_DOWN_MASK));
|
||||
editMenu.add(pasteItem);
|
||||
|
||||
editMenu.addSeparator();
|
||||
|
||||
JMenuItem selectAllItem = new JMenuItem("Vybrat vše");
|
||||
JMenuItem selectAllItem = new JMenuItem("Select All");
|
||||
selectAllItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_A, InputEvent.CTRL_DOWN_MASK));
|
||||
selectAllItem.addActionListener(e -> textArea.selectAll());
|
||||
editMenu.add(selectAllItem);
|
||||
|
||||
editMenu.addSeparator();
|
||||
|
||||
JMenuItem findItem = new JMenuItem("Hledat...");
|
||||
JMenuItem findItem = new JMenuItem("Search...");
|
||||
findItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F, InputEvent.CTRL_DOWN_MASK));
|
||||
findItem.addActionListener(e -> showSearchPanel(true));
|
||||
editMenu.add(findItem);
|
||||
|
||||
JMenuItem findNextItem = new JMenuItem("Hledat další");
|
||||
JMenuItem findNextItem = new JMenuItem("Find Next");
|
||||
findNextItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F3, 0));
|
||||
findNextItem.addActionListener(e -> findNext());
|
||||
editMenu.add(findNextItem);
|
||||
@ -511,8 +511,8 @@ public class FileEditor extends JDialog {
|
||||
|
||||
menuBar.add(editMenu);
|
||||
|
||||
// Menu Nastavení
|
||||
JMenu settingsMenu = new JMenu("Nastavení");
|
||||
// Settings Menu
|
||||
JMenu settingsMenu = new JMenu("Settings");
|
||||
settingsMenu.setMnemonic(java.awt.event.KeyEvent.VK_N);
|
||||
|
||||
JMenuItem fontItem = new JMenuItem("Font...");
|
||||
@ -556,14 +556,14 @@ public class FileEditor extends JDialog {
|
||||
private void setupKeyBindings() {
|
||||
JRootPane rootPane = getRootPane();
|
||||
|
||||
// F2 - Uložit (pouze v editovacím režimu)
|
||||
// F2 - Save (editable mode only)
|
||||
if (!readOnly) {
|
||||
rootPane.registerKeyboardAction(e -> saveFile(),
|
||||
KeyStroke.getKeyStroke(KeyEvent.VK_F2, 0),
|
||||
JComponent.WHEN_IN_FOCUSED_WINDOW);
|
||||
}
|
||||
|
||||
// ESC - Zavřít view
|
||||
// ESC - Close view
|
||||
rootPane.registerKeyboardAction(e -> closeEditor(),
|
||||
KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0),
|
||||
JComponent.WHEN_IN_FOCUSED_WINDOW);
|
||||
@ -626,7 +626,7 @@ public class FileEditor extends JDialog {
|
||||
if (undoManager.canRedo()) undoManager.redo();
|
||||
}, KeyStroke.getKeyStroke(KeyEvent.VK_Y, InputEvent.CTRL_DOWN_MASK), JComponent.WHEN_IN_FOCUSED_WINDOW);
|
||||
|
||||
// Doprava/Doleva a Mezerník pro listování obrázků
|
||||
// Right/Left arrow and Space for image cycling
|
||||
rootPane.registerKeyboardAction(e -> {
|
||||
if (isImageFile(file)) nextImage();
|
||||
}, KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), JComponent.WHEN_IN_FOCUSED_WINDOW);
|
||||
@ -644,7 +644,7 @@ public class FileEditor extends JDialog {
|
||||
if (undoManager.canRedo()) undoManager.redo();
|
||||
}, KeyStroke.getKeyStroke(KeyEvent.VK_Z, InputEvent.CTRL_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK), JComponent.WHEN_IN_FOCUSED_WINDOW);
|
||||
|
||||
// Hledání
|
||||
// Search
|
||||
rootPane.registerKeyboardAction(e -> {
|
||||
if (!isImageFile(file)) showSearchPanel(true);
|
||||
},
|
||||
@ -821,11 +821,11 @@ public class FileEditor extends JDialog {
|
||||
if (readOnly) return;
|
||||
if (!modified) {
|
||||
// No changes - inform the user briefly
|
||||
JOptionPane.showMessageDialog(this, "Žádné změny k uložení.", "Info", JOptionPane.INFORMATION_MESSAGE);
|
||||
JOptionPane.showMessageDialog(this, "No changes to save.", "Info", JOptionPane.INFORMATION_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
int result = showSaveConfirmDialog("Soubor byl změněn. Uložit změny?", "Uložit změny");
|
||||
int result = showSaveConfirmDialog("File was modified. Save changes?", "Save changes");
|
||||
|
||||
if (result == JOptionPane.YES_OPTION) {
|
||||
saveFile();
|
||||
@ -863,7 +863,7 @@ public class FileEditor extends JDialog {
|
||||
updateStatus();
|
||||
return;
|
||||
} catch (IOException e) {
|
||||
JOptionPane.showMessageDialog(this, "Chyba při čtení z archivu: " + e.getMessage(), "Chyba", JOptionPane.ERROR_MESSAGE);
|
||||
JOptionPane.showMessageDialog(this, "Error reading from archive: " + e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
|
||||
dispose();
|
||||
return;
|
||||
}
|
||||
@ -1078,7 +1078,7 @@ public class FileEditor extends JDialog {
|
||||
}
|
||||
|
||||
private void updateTitle() {
|
||||
setTitle((readOnly ? "Prohlížeč - " : "Editor - ") + file.getName() + (modified ? " *" : ""));
|
||||
setTitle((readOnly ? "Viewer - " : "Editor - ") + file.getName() + (modified ? " *" : ""));
|
||||
}
|
||||
|
||||
private void saveFile() {
|
||||
@ -1088,14 +1088,14 @@ public class FileEditor extends JDialog {
|
||||
modified = false;
|
||||
updateTitle();
|
||||
JOptionPane.showMessageDialog(this,
|
||||
"Soubor uložen",
|
||||
"Úspěch",
|
||||
"File saved",
|
||||
"Success",
|
||||
JOptionPane.INFORMATION_MESSAGE);
|
||||
updateStatus();
|
||||
} catch (IOException e) {
|
||||
JOptionPane.showMessageDialog(this,
|
||||
"Chyba při ukládání:\n" + e.getMessage(),
|
||||
"Chyba",
|
||||
"Error during saving:\n" + e.getMessage(),
|
||||
"Error",
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
@ -1113,7 +1113,7 @@ public class FileEditor extends JDialog {
|
||||
private void closeEditor() {
|
||||
saveWindowState();
|
||||
if (!readOnly && modified) {
|
||||
int result = showSaveConfirmDialog("Soubor byl změněn. Uložit změny?", "Neuložené změny");
|
||||
int result = showSaveConfirmDialog("File was modified. Save changes?", "Unsaved changes");
|
||||
|
||||
if (result == JOptionPane.YES_OPTION) {
|
||||
saveFile();
|
||||
@ -1121,7 +1121,7 @@ public class FileEditor extends JDialog {
|
||||
} else if (result == JOptionPane.NO_OPTION) {
|
||||
dispose();
|
||||
}
|
||||
// CANCEL_OPTION - nedělat nic
|
||||
// CANCEL_OPTION - do nothing
|
||||
} else {
|
||||
dispose();
|
||||
}
|
||||
@ -1154,9 +1154,9 @@ public class FileEditor extends JDialog {
|
||||
dlg.add(new JLabel(message), BorderLayout.CENTER);
|
||||
|
||||
JPanel btnPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
|
||||
JButton yesBtn = new JButton("Ano");
|
||||
JButton noBtn = new JButton("Ne");
|
||||
JButton cancelBtn = new JButton("Zrušit");
|
||||
JButton yesBtn = new JButton("Yes");
|
||||
JButton noBtn = new JButton("No");
|
||||
JButton cancelBtn = new JButton("Cancel");
|
||||
|
||||
final int[] result = {JOptionPane.CANCEL_OPTION};
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@ import javax.swing.*;
|
||||
import java.awt.*;
|
||||
|
||||
/**
|
||||
* Dialog pro výběr fontu
|
||||
* Font selection dialog
|
||||
*/
|
||||
public class FontChooserDialog extends JDialog {
|
||||
private Font selectedFont;
|
||||
@ -17,7 +17,7 @@ public class FontChooserDialog extends JDialog {
|
||||
private JTextArea previewArea;
|
||||
|
||||
public FontChooserDialog(Window parent, Font initialFont) {
|
||||
super(parent, "Výběr fontu", ModalityType.APPLICATION_MODAL);
|
||||
super(parent, "Select Font", ModalityType.APPLICATION_MODAL);
|
||||
this.selectedFont = initialFont;
|
||||
|
||||
initComponents();
|
||||
@ -31,10 +31,10 @@ public class FontChooserDialog extends JDialog {
|
||||
private void initComponents() {
|
||||
setLayout(new BorderLayout(10, 10));
|
||||
|
||||
// Panel pro výběr (font, velikost, styl)
|
||||
// Selection panel (font, size, style)
|
||||
JPanel selectionPanel = new JPanel(new GridLayout(1, 3, 10, 0));
|
||||
|
||||
// Seznam fontů
|
||||
// Font list
|
||||
JPanel fontPanel = new JPanel(new BorderLayout());
|
||||
fontPanel.add(new JLabel("Font:"), BorderLayout.NORTH);
|
||||
|
||||
@ -51,9 +51,9 @@ public class FontChooserDialog extends JDialog {
|
||||
JScrollPane fontScroll = new JScrollPane(fontList);
|
||||
fontPanel.add(fontScroll, BorderLayout.CENTER);
|
||||
|
||||
// Seznam velikostí
|
||||
// Size list
|
||||
JPanel sizePanel = new JPanel(new BorderLayout());
|
||||
sizePanel.add(new JLabel("Velikost:"), BorderLayout.NORTH);
|
||||
sizePanel.add(new JLabel("Size:"), BorderLayout.NORTH);
|
||||
|
||||
Integer[] sizes = {8, 9, 10, 11, 12, 13, 14, 16, 18, 20, 22, 24, 26, 28, 32, 36, 40, 48, 56, 64, 72};
|
||||
sizeList = new JList<>(sizes);
|
||||
@ -67,9 +67,9 @@ public class FontChooserDialog extends JDialog {
|
||||
JScrollPane sizeScroll = new JScrollPane(sizeList);
|
||||
sizePanel.add(sizeScroll, BorderLayout.CENTER);
|
||||
|
||||
// Seznam stylů (Plain, Bold, Italic, Bold Italic)
|
||||
// Style list (Plain, Bold, Italic, Bold Italic)
|
||||
JPanel stylePanel = new JPanel(new BorderLayout());
|
||||
stylePanel.add(new JLabel("Styl:"), BorderLayout.NORTH);
|
||||
stylePanel.add(new JLabel("Style:"), BorderLayout.NORTH);
|
||||
|
||||
String[] styles = {"Plain", "Bold", "Italic", "Bold Italic"};
|
||||
styleList = new JList<>(styles);
|
||||
@ -100,7 +100,7 @@ public class FontChooserDialog extends JDialog {
|
||||
previewScroll.setPreferredSize(new Dimension(0, 120));
|
||||
previewPanel.add(previewScroll, BorderLayout.CENTER);
|
||||
|
||||
// Tlačítka
|
||||
// Buttons
|
||||
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
|
||||
|
||||
JButton okButton = new JButton("OK");
|
||||
@ -109,7 +109,7 @@ public class FontChooserDialog extends JDialog {
|
||||
dispose();
|
||||
});
|
||||
|
||||
JButton cancelButton = new JButton("Zrušit");
|
||||
JButton cancelButton = new JButton("Cancel");
|
||||
cancelButton.addActionListener(e -> {
|
||||
approved = false;
|
||||
dispose();
|
||||
@ -129,13 +129,13 @@ public class FontChooserDialog extends JDialog {
|
||||
}
|
||||
|
||||
private void selectFont(Font font) {
|
||||
// Najít a vybrat font
|
||||
// Find and select font
|
||||
fontList.setSelectedValue(font.getName(), true);
|
||||
|
||||
// Najít a vybrat velikost
|
||||
// Find and select size
|
||||
sizeList.setSelectedValue(font.getSize(), true);
|
||||
|
||||
// Najít a vybrat styl
|
||||
// Find and select style
|
||||
int style = font.getStyle();
|
||||
switch (style) {
|
||||
case Font.BOLD:
|
||||
|
||||
@ -452,11 +452,18 @@ public class MainWindow extends JFrame {
|
||||
|
||||
// Search button
|
||||
JButton btnSearch = new JButton("🔍");
|
||||
btnSearch.setToolTipText("Hledat soubory (Alt+F7)");
|
||||
btnSearch.setToolTipText("Search files (Alt+F7)");
|
||||
btnSearch.setFocusable(false);
|
||||
btnSearch.addActionListener(e -> showSearchDialog());
|
||||
toolBar.add(btnSearch);
|
||||
|
||||
// Sync button
|
||||
JButton btnSync = new JButton("⚖");
|
||||
btnSync.setToolTipText("Compare directories");
|
||||
btnSync.setFocusable(false);
|
||||
btnSync.addActionListener(e -> showSyncDialog());
|
||||
toolBar.add(btnSync);
|
||||
|
||||
toolBar.addSeparator();
|
||||
|
||||
// Load custom shortcuts from config
|
||||
@ -1725,6 +1732,16 @@ public class MainWindow extends JFrame {
|
||||
dialog.setVisible(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show Sync Directories dialog
|
||||
*/
|
||||
private void showSyncDialog() {
|
||||
File leftDir = leftPanel.getCurrentDirectory();
|
||||
File rightDir = rightPanel.getCurrentDirectory();
|
||||
SyncDirectoriesDialog dialog = new SyncDirectoriesDialog(this, leftDir, rightDir, config);
|
||||
dialog.setVisible(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show wildcard select dialog
|
||||
*/
|
||||
|
||||
@ -124,7 +124,7 @@ public class SearchDialog extends JDialog {
|
||||
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 0;
|
||||
searchPanel.add(new JLabel("Hledat:"), gbc);
|
||||
searchPanel.add(new JLabel("Search for:"), gbc);
|
||||
|
||||
gbc.gridx = 1;
|
||||
gbc.weightx = 1.0;
|
||||
@ -231,7 +231,7 @@ public class SearchDialog extends JDialog {
|
||||
});
|
||||
|
||||
JScrollPane scrollPane = new JScrollPane(resultsTable);
|
||||
scrollPane.setBorder(BorderFactory.createTitledBorder("Výsledky"));
|
||||
scrollPane.setBorder(BorderFactory.createTitledBorder("Results"));
|
||||
add(scrollPane, BorderLayout.CENTER);
|
||||
|
||||
// Status bar with progress and message
|
||||
@ -244,24 +244,24 @@ public class SearchDialog extends JDialog {
|
||||
statusPanel.add(statusLabel, BorderLayout.CENTER);
|
||||
statusPanel.add(statusProgressBar, BorderLayout.EAST);
|
||||
|
||||
// Panel s tlačítky
|
||||
// Panel with buttons
|
||||
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
|
||||
|
||||
searchButton = new JButton("Hledat");
|
||||
searchButton = new JButton("Search");
|
||||
searchButton.addActionListener(e -> performSearch());
|
||||
|
||||
viewButton = new JButton("Zobrazit");
|
||||
viewButton = new JButton("View");
|
||||
viewButton.setEnabled(false);
|
||||
viewButton.addActionListener(e -> viewSelectedFile());
|
||||
|
||||
editButton = new JButton("Upravit");
|
||||
editButton = new JButton("Edit");
|
||||
editButton.setEnabled(false);
|
||||
editButton.addActionListener(e -> editSelectedFile());
|
||||
|
||||
cancelButton = new JButton("Zavřít");
|
||||
cancelButton = new JButton("Close");
|
||||
cancelButton.addActionListener(e -> dispose());
|
||||
|
||||
JButton openButton = new JButton("Otevřít umístění");
|
||||
JButton openButton = new JButton("Open location");
|
||||
openButton.addActionListener(e -> openSelectedFile());
|
||||
|
||||
buttonPanel.add(searchButton);
|
||||
@ -571,15 +571,15 @@ public class SearchDialog extends JDialog {
|
||||
statusLabel.setText("Done — found " + foundCount + " files");
|
||||
if (tableModel.getRowCount() == 0) {
|
||||
JOptionPane.showMessageDialog(SearchDialog.this,
|
||||
"Nebyly nalezeny žádné soubory",
|
||||
"Výsledek",
|
||||
"No files found",
|
||||
"Result",
|
||||
JOptionPane.INFORMATION_MESSAGE);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
statusLabel.setText("Error");
|
||||
JOptionPane.showMessageDialog(SearchDialog.this,
|
||||
"Chyba při hledání: " + e.getMessage(),
|
||||
"Chyba",
|
||||
"Error during search: " + e.getMessage(),
|
||||
"Error",
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
@ -589,7 +589,7 @@ public class SearchDialog extends JDialog {
|
||||
}
|
||||
|
||||
/**
|
||||
* Otevře umístění vybraného souboru v exploreru
|
||||
* Open selected file location
|
||||
*/
|
||||
private void openSelectedFile() {
|
||||
int selectedRow = resultsTable.getSelectedRow();
|
||||
@ -622,8 +622,8 @@ public class SearchDialog extends JDialog {
|
||||
Desktop.getDesktop().open(item.getFile().getParentFile());
|
||||
} catch (Exception e) {
|
||||
JOptionPane.showMessageDialog(this,
|
||||
"Nepodařilo se otevřít umístění: " + e.getMessage(),
|
||||
"Chyba",
|
||||
"Could not open location: " + e.getMessage(),
|
||||
"Error",
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
@ -666,7 +666,7 @@ public class SearchDialog extends JDialog {
|
||||
});
|
||||
viewer.setVisible(true);
|
||||
} catch (Exception e) {
|
||||
JOptionPane.showMessageDialog(this, "Chyba při otevírání souboru: " + e.getMessage(), "Chyba", JOptionPane.ERROR_MESSAGE);
|
||||
JOptionPane.showMessageDialog(this, "Error opening file: " + e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -682,7 +682,7 @@ public class SearchDialog extends JDialog {
|
||||
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);
|
||||
JOptionPane.showMessageDialog(this, "Editing web files directly in an archive is not supported. Use 'Go to File' to access the archive.", "Information", JOptionPane.INFORMATION_MESSAGE);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
@ -710,18 +710,18 @@ public class SearchDialog extends JDialog {
|
||||
});
|
||||
editor.setVisible(true);
|
||||
} catch (Exception e) {
|
||||
JOptionPane.showMessageDialog(this, "Chyba při otevírání souboru: " + e.getMessage(), "Chyba", JOptionPane.ERROR_MESSAGE);
|
||||
JOptionPane.showMessageDialog(this, "Error opening file: " + e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Model tabulky pro výsledky hledání
|
||||
* Table model for search results
|
||||
*/
|
||||
private class ResultsTableModel extends AbstractTableModel {
|
||||
|
||||
private final String[] columnNames = {"Cesta", "Velikost", "Datum změny"};
|
||||
private final String[] columnNames = {"Path", "Size", "Modified Date"};
|
||||
private List<FileItem> results = new ArrayList<>();
|
||||
|
||||
public void addResult(FileItem item) {
|
||||
|
||||
414
src/main/java/cz/kamma/kfmanager/ui/SyncDirectoriesDialog.java
Normal file
414
src/main/java/cz/kamma/kfmanager/ui/SyncDirectoriesDialog.java
Normal file
@ -0,0 +1,414 @@
|
||||
package cz.kamma.kfmanager.ui;
|
||||
|
||||
import cz.kamma.kfmanager.config.AppConfig;
|
||||
import cz.kamma.kfmanager.model.FileItem;
|
||||
import cz.kamma.kfmanager.service.FileOperations;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.table.AbstractTableModel;
|
||||
import javax.swing.table.DefaultTableCellRenderer;
|
||||
import java.awt.*;
|
||||
import java.awt.event.WindowAdapter;
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
import java.util.TreeMap;
|
||||
|
||||
public class SyncDirectoriesDialog extends JDialog {
|
||||
private JTextField leftPathField;
|
||||
private JTextField rightPathField;
|
||||
private JCheckBox subdirsCheckbox;
|
||||
private JCheckBox byContentCheckbox;
|
||||
private JCheckBox ignoreDateCheckbox;
|
||||
private JToggleButton showLeftOnlyBtn;
|
||||
private JToggleButton showEqualBtn;
|
||||
private JToggleButton showDiffBtn;
|
||||
private JToggleButton showRightOnlyBtn;
|
||||
private List<SyncEntry> allEntries = new ArrayList<>();
|
||||
private JTable resultsTable;
|
||||
private SyncTableModel tableModel;
|
||||
private AppConfig config;
|
||||
private JLabel statusLabel;
|
||||
|
||||
public SyncDirectoriesDialog(Frame owner, File leftDir, File rightDir, AppConfig config) {
|
||||
super(owner, "Synchronize Directories", true);
|
||||
this.config = config;
|
||||
|
||||
initComponents(leftDir, rightDir);
|
||||
setSize(1000, 600);
|
||||
setLocationRelativeTo(owner);
|
||||
}
|
||||
|
||||
private void initComponents(File leftDir, File rightDir) {
|
||||
setLayout(new BorderLayout());
|
||||
|
||||
JPanel topPanel = new JPanel();
|
||||
topPanel.setLayout(new BoxLayout(topPanel, BoxLayout.Y_AXIS));
|
||||
topPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
|
||||
|
||||
// Paths row
|
||||
JPanel pathsPanel = new JPanel(new GridLayout(1, 2, 10, 0));
|
||||
|
||||
JPanel leftP = new JPanel(new BorderLayout(5, 0));
|
||||
leftPathField = new JTextField(leftDir != null ? leftDir.getAbsolutePath() : "");
|
||||
JButton browseLeft = new JButton("...");
|
||||
browseLeft.addActionListener(e -> browsePath(leftPathField));
|
||||
leftP.add(leftPathField, BorderLayout.CENTER);
|
||||
leftP.add(browseLeft, BorderLayout.EAST);
|
||||
|
||||
JPanel rightP = new JPanel(new BorderLayout(5, 0));
|
||||
rightPathField = new JTextField(rightDir != null ? rightDir.getAbsolutePath() : "");
|
||||
JButton browseRight = new JButton("...");
|
||||
browseRight.addActionListener(e -> browsePath(rightPathField));
|
||||
rightP.add(rightPathField, BorderLayout.CENTER);
|
||||
rightP.add(browseRight, BorderLayout.EAST);
|
||||
|
||||
pathsPanel.add(leftP);
|
||||
pathsPanel.add(rightP);
|
||||
topPanel.add(pathsPanel);
|
||||
topPanel.add(Box.createVerticalStrut(10));
|
||||
|
||||
// Options toolbar
|
||||
JPanel optionsPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
|
||||
JButton compareBtn = new JButton("Compare");
|
||||
compareBtn.addActionListener(e -> performComparison());
|
||||
optionsPanel.add(compareBtn);
|
||||
|
||||
subdirsCheckbox = new JCheckBox("Include subdirectories");
|
||||
byContentCheckbox = new JCheckBox("By content");
|
||||
ignoreDateCheckbox = new JCheckBox("Ignore date");
|
||||
|
||||
optionsPanel.add(subdirsCheckbox);
|
||||
optionsPanel.add(byContentCheckbox);
|
||||
optionsPanel.add(ignoreDateCheckbox);
|
||||
|
||||
optionsPanel.add(Box.createHorizontalStrut(20));
|
||||
optionsPanel.add(new JLabel("Show:"));
|
||||
|
||||
showLeftOnlyBtn = new JToggleButton("➡");
|
||||
showEqualBtn = new JToggleButton("=");
|
||||
showDiffBtn = new JToggleButton("≠");
|
||||
showRightOnlyBtn = new JToggleButton("⬅");
|
||||
|
||||
showLeftOnlyBtn.setSelected(true);
|
||||
showEqualBtn.setSelected(true);
|
||||
showDiffBtn.setSelected(true);
|
||||
showRightOnlyBtn.setSelected(true);
|
||||
|
||||
java.awt.event.ActionListener filterAl = e -> applyFilters();
|
||||
showLeftOnlyBtn.addActionListener(filterAl);
|
||||
showEqualBtn.addActionListener(filterAl);
|
||||
showDiffBtn.addActionListener(filterAl);
|
||||
showRightOnlyBtn.addActionListener(filterAl);
|
||||
|
||||
optionsPanel.add(showLeftOnlyBtn);
|
||||
optionsPanel.add(showEqualBtn);
|
||||
optionsPanel.add(showDiffBtn);
|
||||
optionsPanel.add(showRightOnlyBtn);
|
||||
|
||||
optionsPanel.add(Box.createHorizontalStrut(10));
|
||||
JButton duplicatesBtn = new JButton("duplicates");
|
||||
duplicatesBtn.addActionListener(e -> {
|
||||
showEqualBtn.setSelected(true);
|
||||
showDiffBtn.setSelected(true);
|
||||
showLeftOnlyBtn.setSelected(false);
|
||||
showRightOnlyBtn.setSelected(false);
|
||||
applyFilters();
|
||||
});
|
||||
JButton singlesBtn = new JButton("singles");
|
||||
singlesBtn.addActionListener(e -> {
|
||||
showEqualBtn.setSelected(false);
|
||||
showDiffBtn.setSelected(false);
|
||||
showLeftOnlyBtn.setSelected(true);
|
||||
showRightOnlyBtn.setSelected(true);
|
||||
applyFilters();
|
||||
});
|
||||
optionsPanel.add(duplicatesBtn);
|
||||
optionsPanel.add(singlesBtn);
|
||||
|
||||
topPanel.add(optionsPanel);
|
||||
add(topPanel, BorderLayout.NORTH);
|
||||
|
||||
// Table
|
||||
tableModel = new SyncTableModel();
|
||||
resultsTable = new JTable(tableModel);
|
||||
resultsTable.setShowGrid(false);
|
||||
resultsTable.setIntercellSpacing(new Dimension(0, 0));
|
||||
resultsTable.setDefaultRenderer(Object.class, new SyncTableCellRenderer());
|
||||
|
||||
// Adjust column widths
|
||||
resultsTable.getColumnModel().getColumn(3).setMaxWidth(40); // Icon column
|
||||
|
||||
add(new JScrollPane(resultsTable), BorderLayout.CENTER);
|
||||
|
||||
// Bottom panel
|
||||
JPanel bottomPanel = new JPanel(new BorderLayout());
|
||||
statusLabel = new JLabel("Waiting for comparison...");
|
||||
statusLabel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
|
||||
bottomPanel.add(statusLabel, BorderLayout.WEST);
|
||||
|
||||
JPanel btnPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
|
||||
JButton syncBtn = new JButton("Synchronize...");
|
||||
syncBtn.addActionListener(e -> performSync());
|
||||
JButton closeBtn = new JButton("Close");
|
||||
closeBtn.addActionListener(e -> dispose());
|
||||
btnPanel.add(syncBtn);
|
||||
btnPanel.add(closeBtn);
|
||||
bottomPanel.add(btnPanel, BorderLayout.EAST);
|
||||
|
||||
add(bottomPanel, BorderLayout.SOUTH);
|
||||
}
|
||||
|
||||
private void browsePath(JTextField field) {
|
||||
JFileChooser chooser = new JFileChooser(field.getText());
|
||||
chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
|
||||
if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
|
||||
field.setText(chooser.getSelectedFile().getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
private void performComparison() {
|
||||
File leftDir = new File(leftPathField.getText());
|
||||
File rightDir = new File(rightPathField.getText());
|
||||
|
||||
if (!leftDir.isDirectory() || !rightDir.isDirectory()) {
|
||||
JOptionPane.showMessageDialog(this, "Both paths must be directories.", "Error", JOptionPane.ERROR_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
statusLabel.setText("Comparing...");
|
||||
|
||||
new SwingWorker<List<SyncEntry>, Void>() {
|
||||
@Override
|
||||
protected List<SyncEntry> doInBackground() throws Exception {
|
||||
return compareDirectories(leftDir, rightDir, "", subdirsCheckbox.isSelected());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void done() {
|
||||
try {
|
||||
allEntries = get();
|
||||
applyFilters();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
JOptionPane.showMessageDialog(SyncDirectoriesDialog.this, "Error during comparison: " + e.getMessage());
|
||||
statusLabel.setText("Error.");
|
||||
}
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
|
||||
private List<SyncEntry> compareDirectories(File leftRoot, File rightRoot, String relativePath, boolean recursive) {
|
||||
Map<String, SyncEntry> map = new TreeMap<>();
|
||||
|
||||
File leftDir = relativePath.isEmpty() ? leftRoot : new File(leftRoot, relativePath);
|
||||
File rightDir = relativePath.isEmpty() ? rightRoot : new File(rightRoot, relativePath);
|
||||
|
||||
File[] leftFiles = leftDir.listFiles();
|
||||
File[] rightFiles = rightDir.listFiles();
|
||||
|
||||
if (leftFiles != null) {
|
||||
for (File f : leftFiles) {
|
||||
String name = relativePath.isEmpty() ? f.getName() : relativePath + File.separator + f.getName();
|
||||
SyncEntry entry = new SyncEntry(name);
|
||||
entry.leftFile = f;
|
||||
entry.leftSize = f.isDirectory() ? -1 : f.length();
|
||||
entry.leftModified = f.lastModified();
|
||||
entry.isDirectory = f.isDirectory();
|
||||
map.put(name, entry);
|
||||
}
|
||||
}
|
||||
|
||||
if (rightFiles != null) {
|
||||
for (File f : rightFiles) {
|
||||
String name = relativePath.isEmpty() ? f.getName() : relativePath + File.separator + f.getName();
|
||||
SyncEntry entry = map.get(name);
|
||||
if (entry == null) {
|
||||
entry = new SyncEntry(name);
|
||||
map.put(name, entry);
|
||||
}
|
||||
entry.rightFile = f;
|
||||
entry.rightSize = f.isDirectory() ? -1 : f.length();
|
||||
entry.rightModified = f.lastModified();
|
||||
entry.isDirectory = entry.isDirectory || f.isDirectory();
|
||||
}
|
||||
}
|
||||
|
||||
List<SyncEntry> resultList = new ArrayList<>();
|
||||
List<String> subdirsToProcess = new ArrayList<>();
|
||||
|
||||
for (SyncEntry entry : map.values()) {
|
||||
if (entry.isDirectory) {
|
||||
if (recursive) {
|
||||
subdirsToProcess.add(entry.relativePath);
|
||||
}
|
||||
} else {
|
||||
updateRelation(entry);
|
||||
resultList.add(entry);
|
||||
}
|
||||
}
|
||||
|
||||
if (recursive) {
|
||||
for (String sub : subdirsToProcess) {
|
||||
resultList.addAll(compareDirectories(leftRoot, rightRoot, sub, true));
|
||||
}
|
||||
}
|
||||
|
||||
return resultList;
|
||||
}
|
||||
|
||||
private void applyFilters() {
|
||||
List<SyncEntry> filtered = new ArrayList<>();
|
||||
for (SyncEntry e : allEntries) {
|
||||
if ("=".equals(e.relation) && showEqualBtn.isSelected()) filtered.add(e);
|
||||
else if ("!=".equals(e.relation) && showDiffBtn.isSelected()) filtered.add(e);
|
||||
else if ("->".equals(e.relation) && showLeftOnlyBtn.isSelected()) filtered.add(e);
|
||||
else if ("<-".equals(e.relation) && showRightOnlyBtn.isSelected()) filtered.add(e);
|
||||
}
|
||||
tableModel.setResults(filtered);
|
||||
statusLabel.setText(String.format("Showing %d of %d items.", filtered.size(), allEntries.size()));
|
||||
}
|
||||
|
||||
private void updateRelation(SyncEntry entry) {
|
||||
if (entry.leftFile != null && entry.rightFile == null) {
|
||||
entry.relation = "->";
|
||||
} else if (entry.leftFile == null && entry.rightFile != null) {
|
||||
entry.relation = "<-";
|
||||
} else {
|
||||
boolean sizeMatch = entry.leftSize == entry.rightSize;
|
||||
boolean dateMatch = ignoreDateCheckbox.isSelected() || (entry.leftModified / 1000 == entry.rightModified / 1000);
|
||||
|
||||
if (sizeMatch && dateMatch) {
|
||||
if (byContentCheckbox.isSelected()) {
|
||||
if (compareByContent(entry.leftFile, entry.rightFile)) {
|
||||
entry.relation = "=";
|
||||
} else {
|
||||
entry.relation = "!=";
|
||||
}
|
||||
} else {
|
||||
entry.relation = "=";
|
||||
}
|
||||
} else {
|
||||
if (entry.leftModified > entry.rightModified) {
|
||||
entry.relation = "->";
|
||||
} else if (entry.leftModified < entry.rightModified) {
|
||||
entry.relation = "<-";
|
||||
} else {
|
||||
entry.relation = "!=";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean compareByContent(File f1, File f2) {
|
||||
if (f1.length() != f2.length()) return false;
|
||||
try (java.io.InputStream i1 = new java.io.FileInputStream(f1);
|
||||
java.io.InputStream i2 = new java.io.FileInputStream(f2)) {
|
||||
byte[] b1 = new byte[8192];
|
||||
byte[] b2 = new byte[8192];
|
||||
int n;
|
||||
while ((n = i1.read(b1)) != -1) {
|
||||
if (i2.read(b2) != n) return false;
|
||||
if (!java.util.Arrays.equals(b1, 0, n, b2, 0, n)) return false;
|
||||
}
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void performSync() {
|
||||
// Basic implementation of sync (copy missing/newer files)
|
||||
// For now just show info, actual copy logic would use FileOperationQueue
|
||||
JOptionPane.showMessageDialog(this, "Synchronization feature will be implemented soon. For now, only comparison is available.", "Information", JOptionPane.INFORMATION_MESSAGE);
|
||||
}
|
||||
|
||||
private static class SyncEntry {
|
||||
String relativePath;
|
||||
File leftFile;
|
||||
File rightFile;
|
||||
long leftSize;
|
||||
long rightSize;
|
||||
long leftModified;
|
||||
long rightModified;
|
||||
String relation = ""; // "=", "!=", "->", "<-"
|
||||
boolean isDirectory;
|
||||
|
||||
SyncEntry(String rel) { this.relativePath = rel; }
|
||||
}
|
||||
|
||||
private static class SyncTableModel extends AbstractTableModel {
|
||||
private String[] columns = {"Name (L)", "Size", "Date", "", "Date", "Size", "Name (R)"};
|
||||
private List<SyncEntry> entries = new ArrayList<>();
|
||||
|
||||
public void setResults(List<SyncEntry> results) {
|
||||
this.entries = results;
|
||||
fireTableDataChanged();
|
||||
}
|
||||
|
||||
@Override public int getRowCount() { return entries.size(); }
|
||||
@Override public int getColumnCount() { return columns.length; }
|
||||
@Override public String getColumnName(int col) { return columns[col]; }
|
||||
|
||||
@Override
|
||||
public Object getValueAt(int row, int col) {
|
||||
SyncEntry e = entries.get(row);
|
||||
switch(col) {
|
||||
case 0: return e.leftFile != null ? e.relativePath : "";
|
||||
case 1: return e.leftFile != null ? formatSize(e.leftSize) : "";
|
||||
case 2: return e.leftFile != null ? formatDate(e.leftModified) : "";
|
||||
case 3: return e.relation;
|
||||
case 4: return e.rightFile != null ? formatDate(e.rightModified) : "";
|
||||
case 5: return e.rightFile != null ? formatSize(e.rightSize) : "";
|
||||
case 6: return e.rightFile != null ? e.relativePath : "";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
private String formatSize(long size) {
|
||||
if (size < 0) return "<DIR>";
|
||||
return String.format("%,d", size);
|
||||
}
|
||||
|
||||
private String formatDate(long time) {
|
||||
return new java.text.SimpleDateFormat("dd.MM.yy HH:mm:ss").format(new Date(time));
|
||||
}
|
||||
}
|
||||
|
||||
private static class SyncTableCellRenderer extends DefaultTableCellRenderer {
|
||||
@Override
|
||||
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSel, boolean hasFocus, int row, int col) {
|
||||
Component c = super.getTableCellRendererComponent(table, value, isSel, hasFocus, row, col);
|
||||
SyncTableModel model = (SyncTableModel) table.getModel();
|
||||
SyncEntry entry = model.entries.get(row);
|
||||
|
||||
if (!isSel) {
|
||||
if ("=".equals(entry.relation)) {
|
||||
c.setForeground(Color.BLACK);
|
||||
} else if ("!=".equals(entry.relation)) {
|
||||
c.setForeground(Color.RED);
|
||||
} else if ("->".equals(entry.relation)) {
|
||||
c.setForeground(new Color(0, 100, 0)); // Dark green
|
||||
} else if ("<-".equals(entry.relation)) {
|
||||
c.setForeground(Color.BLUE);
|
||||
}
|
||||
}
|
||||
|
||||
if (col == 3) {
|
||||
setHorizontalAlignment(CENTER);
|
||||
setFont(getFont().deriveFont(Font.BOLD));
|
||||
} else if (col == 1 || col == 5) {
|
||||
setHorizontalAlignment(RIGHT);
|
||||
} else {
|
||||
setHorizontalAlignment(LEFT);
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -14,7 +14,7 @@ public class WildcardSelectDialog extends JDialog {
|
||||
private boolean confirmed = false;
|
||||
|
||||
public WildcardSelectDialog(Frame parent) {
|
||||
super(parent, "Vybrat podle masky", true);
|
||||
super(parent, "Select by pattern", true);
|
||||
initComponents();
|
||||
|
||||
MainApp.applyReflectiveCaretColor(getContentPane());
|
||||
@ -31,7 +31,7 @@ public class WildcardSelectDialog extends JDialog {
|
||||
mainPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
|
||||
|
||||
// Label
|
||||
JLabel label = new JLabel("Zadejte masku (např. *.txt, test*.*, file?.dat):");
|
||||
JLabel label = new JLabel("Enter pattern (e.g. *.txt, test*.*, file?.dat):");
|
||||
mainPanel.add(label, BorderLayout.NORTH);
|
||||
|
||||
// Text field for pattern
|
||||
@ -50,7 +50,7 @@ public class WildcardSelectDialog extends JDialog {
|
||||
dispose();
|
||||
});
|
||||
|
||||
JButton cancelButton = new JButton("Zrušit");
|
||||
JButton cancelButton = new JButton("Cancel");
|
||||
cancelButton.addActionListener(e -> {
|
||||
confirmed = false;
|
||||
dispose();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user