ftp improved
This commit is contained in:
parent
a810cfac02
commit
14d386f736
@ -489,6 +489,17 @@ public class FtpService {
|
||||
return pwd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Explicitly close connection for a profile if any is cached.
|
||||
* Note: Current infrastructure creates/closes connections per operation,
|
||||
* but this provides a hook for future persistent connection management.
|
||||
*/
|
||||
public static void disconnect(FtpProfile profile) {
|
||||
if (profile == null) return;
|
||||
log("DISCONNECT requested for profile: " + profile.getName() + " (" + profile.getHost() + ")");
|
||||
// When persistent connections are implemented, they should be closed here.
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse an FTP URL (ftp://host:port/path) and find matching profile from AppConfig.
|
||||
*/
|
||||
@ -527,7 +538,7 @@ public class FtpService {
|
||||
return FTP_PREFIX + profile.getHost() + ":" + profile.getPort() + remotePath;
|
||||
}
|
||||
|
||||
private static String getNameFromPath(String path) {
|
||||
public static String getNameFromPath(String path) {
|
||||
if (path == null || path.equals("/") || path.isEmpty()) return "";
|
||||
int lastSlash = path.lastIndexOf('/');
|
||||
return path.substring(lastSlash + 1);
|
||||
|
||||
@ -29,6 +29,12 @@ public class FileEditor extends JFrame {
|
||||
private boolean readOnly;
|
||||
private JLabel statusPosLabel;
|
||||
private JLabel statusSelLabel;
|
||||
|
||||
// FTP upload support
|
||||
private cz.kamma.kfmanager.model.FtpProfile ftpProfile;
|
||||
private String ftpPath;
|
||||
private Runnable onSaveSuccess;
|
||||
|
||||
// Hex view support
|
||||
private boolean hexMode = false;
|
||||
private byte[] fileBytes = null;
|
||||
@ -112,6 +118,15 @@ public class FileEditor extends JFrame {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Set FTP details for automatic upload after saving.
|
||||
*/
|
||||
public void setFtpDetails(cz.kamma.kfmanager.model.FtpProfile profile, String remotePath, Runnable onSaveSuccess) {
|
||||
this.ftpProfile = profile;
|
||||
this.ftpPath = remotePath;
|
||||
this.onSaveSuccess = onSaveSuccess;
|
||||
}
|
||||
|
||||
private void initSearchPanel() {
|
||||
searchPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 5, 2));
|
||||
searchPanel.setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, Color.GRAY));
|
||||
@ -959,7 +974,7 @@ public class FileEditor extends JFrame {
|
||||
}
|
||||
|
||||
private void loadFile() {
|
||||
if (virtualPath != null) {
|
||||
if (virtualPath != null && ftpProfile == null) {
|
||||
try {
|
||||
fileBytes = cz.kamma.kfmanager.service.FileOperations.readFileFromArchive(file, virtualPath);
|
||||
boolean binary = isBinary(fileBytes);
|
||||
@ -1212,6 +1227,40 @@ public class FileEditor extends JFrame {
|
||||
Files.write(file.toPath(), content.getBytes("UTF-8"));
|
||||
modified = false;
|
||||
updateTitle();
|
||||
|
||||
if (ftpProfile != null && ftpPath != null) {
|
||||
final cz.kamma.kfmanager.model.FtpProfile finalProfile = ftpProfile;
|
||||
final String finalPath = ftpPath;
|
||||
final File finalFile = file;
|
||||
|
||||
// Use the main window's common operation runner if possible, or just background it here.
|
||||
// Since FileEditor works standalone too, we'll use a simple SwingWorker.
|
||||
new SwingWorker<Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground() throws Exception {
|
||||
cz.kamma.kfmanager.service.FtpService.uploadFile(finalProfile, finalFile, finalPath, null);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void done() {
|
||||
try {
|
||||
get();
|
||||
if (onSaveSuccess != null) {
|
||||
onSaveSuccess.run();
|
||||
}
|
||||
JOptionPane.showMessageDialog(FileEditor.this, "File saved and uploaded to FTP", "Success", JOptionPane.INFORMATION_MESSAGE);
|
||||
} catch (Exception ex) {
|
||||
JOptionPane.showMessageDialog(FileEditor.this,
|
||||
"Successfully saved locally, but failed to upload to FTP:\n" + ex.getMessage(),
|
||||
"FTP Upload Error",
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
}.execute();
|
||||
return; // message handled in SwingWorker
|
||||
}
|
||||
|
||||
JOptionPane.showMessageDialog(this,
|
||||
"File saved",
|
||||
"Success",
|
||||
|
||||
@ -440,6 +440,8 @@ public class FilePanel extends JPanel {
|
||||
// Extract display name from FTP URL for tab title
|
||||
String tabTitle = getFtpTabTitle(ftpPath);
|
||||
|
||||
applyConfiguredAppearance(tab);
|
||||
|
||||
tabbedPane.addTab(tabTitle, tab);
|
||||
tabbedPane.setSelectedComponent(tab);
|
||||
|
||||
@ -788,9 +790,11 @@ public class FilePanel extends JPanel {
|
||||
Component comp = tabbedPane.getComponentAt(i);
|
||||
if (comp instanceof FilePanelTab tab && tab.isFtpTab()) {
|
||||
if (tabbedPane.getTabCount() > 1) {
|
||||
tab.cleanup();
|
||||
tabbedPane.removeTabAt(i);
|
||||
} else {
|
||||
// If it's the last tab, reset it to home instead of removing
|
||||
tab.cleanup();
|
||||
tab.loadDirectory(new File(System.getProperty("user.home")));
|
||||
}
|
||||
}
|
||||
@ -805,6 +809,10 @@ public class FilePanel extends JPanel {
|
||||
public void closeCurrentTab() {
|
||||
int index = tabbedPane.getSelectedIndex();
|
||||
if (index >= 0 && tabbedPane.getTabCount() > 1) {
|
||||
Component comp = tabbedPane.getComponentAt(index);
|
||||
if (comp instanceof FilePanelTab tab) {
|
||||
tab.cleanup();
|
||||
}
|
||||
tabbedPane.removeTabAt(index);
|
||||
updatePathField();
|
||||
updateTabStyles();
|
||||
|
||||
@ -1624,8 +1624,14 @@ public class FilePanelTab extends JPanel {
|
||||
*/
|
||||
public void navigateFtpUp() {
|
||||
if (ftpCurrentPath == null || ftpCurrentPath.equals("/")) return;
|
||||
String previousDirName = FtpService.getNameFromPath(ftpCurrentPath);
|
||||
String parentPath = FtpService.getParentPath(ftpCurrentPath);
|
||||
loadFtpDirectory(parentPath, true, true);
|
||||
loadFtpDirectory(parentPath, false, true);
|
||||
|
||||
// Select the directory we just left
|
||||
if (previousDirName != null && !previousDirName.isEmpty()) {
|
||||
SwingUtilities.invokeLater(() -> selectItemByName(previousDirName));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -4323,6 +4329,15 @@ public class FilePanelTab extends JPanel {
|
||||
updateSortButtonsDisplay();
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleanup resources when tab is closed.
|
||||
*/
|
||||
public void cleanup() {
|
||||
if (isFtpTab && ftpProfile != null) {
|
||||
FtpService.disconnect(ftpProfile);
|
||||
}
|
||||
}
|
||||
|
||||
// Getters
|
||||
public JTable getFileTable() {
|
||||
return fileTable;
|
||||
|
||||
@ -17,6 +17,7 @@ import java.awt.datatransfer.DataFlavor;
|
||||
import java.awt.event.*;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@ -2437,6 +2438,30 @@ public class MainWindow extends JFrame {
|
||||
return;
|
||||
}
|
||||
|
||||
if (item.isFtp()) {
|
||||
try {
|
||||
File tempFile = Files.createTempFile("kf-ftp-view-", "-" + item.getName()).toFile();
|
||||
tempFile.deleteOnExit();
|
||||
|
||||
performFileOperation((cb) -> {
|
||||
cz.kamma.kfmanager.service.FtpService.downloadFile(item.getFtpProfile(), item.getFtpPath(), tempFile, cb);
|
||||
}, "Download finished", false, false, () -> {
|
||||
FileEditor viewer = new FileEditor(this, tempFile, item.getFtpPath(), config, true);
|
||||
viewer.addWindowListener(new java.awt.event.WindowAdapter() {
|
||||
@Override
|
||||
public void windowClosed(java.awt.event.WindowEvent e) {
|
||||
try { tempFile.delete(); } catch(Exception ignore) {}
|
||||
requestFocusInActivePanel();
|
||||
}
|
||||
});
|
||||
viewer.setVisible(true);
|
||||
});
|
||||
} catch (IOException e) {
|
||||
JOptionPane.showMessageDialog(this, "Could not create temp file: " + e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
File file = item.getFile();
|
||||
|
||||
// Removed previous 10 MB limit: allow opening large files in the viewer (paged hex will stream large binaries).
|
||||
@ -2465,6 +2490,49 @@ public class MainWindow extends JFrame {
|
||||
return;
|
||||
}
|
||||
|
||||
if (item.isFtp()) {
|
||||
try {
|
||||
File tempFile = Files.createTempFile("kf-ftp-edit-", "-" + item.getName()).toFile();
|
||||
tempFile.deleteOnExit();
|
||||
|
||||
performFileOperation((cb) -> {
|
||||
cz.kamma.kfmanager.service.FtpService.downloadFile(item.getFtpProfile(), item.getFtpPath(), tempFile, cb);
|
||||
}, "Download finished", false, false, () -> {
|
||||
// Check if an external editor is configured
|
||||
String ext = config.getExternalEditorPath();
|
||||
if (ext != null && !ext.trim().isEmpty()) {
|
||||
try {
|
||||
java.util.List<String> cmd = new java.util.ArrayList<>();
|
||||
cmd.add(ext);
|
||||
cmd.add(tempFile.getAbsolutePath());
|
||||
Process p = new ProcessBuilder(cmd).start();
|
||||
// Optional: Monitor process to upload back on exit?
|
||||
// For now internal editor is safer for auto-upload.
|
||||
} catch (Exception ex) {
|
||||
JOptionPane.showMessageDialog(this, "External editor failed: " + ex.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
|
||||
FileEditor editor = new FileEditor(this, tempFile, item.getFtpPath(), config, false);
|
||||
editor.setFtpDetails(item.getFtpProfile(), item.getFtpPath(), () -> {
|
||||
// Success callback after upload
|
||||
if (activePanel != null) activePanel.refresh(false);
|
||||
});
|
||||
editor.addWindowListener(new java.awt.event.WindowAdapter() {
|
||||
@Override
|
||||
public void windowClosed(java.awt.event.WindowEvent e) {
|
||||
try { tempFile.delete(); } catch(Exception ignore) {}
|
||||
requestFocusInActivePanel();
|
||||
}
|
||||
});
|
||||
editor.setVisible(true);
|
||||
});
|
||||
} catch (IOException e) {
|
||||
JOptionPane.showMessageDialog(this, "Could not create temp file: " + e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
File file = item.getFile();
|
||||
|
||||
// If an external editor is configured, try launching it with the file path
|
||||
@ -3302,9 +3370,7 @@ public class MainWindow extends JFrame {
|
||||
MainWindow.this.toFront();
|
||||
|
||||
for (FilePanel panel : panelsToRefresh) {
|
||||
if (panel.getCurrentDirectory() != null) {
|
||||
panel.loadDirectory(panel.getCurrentDirectory(), false, false);
|
||||
}
|
||||
panel.refresh(false);
|
||||
}
|
||||
|
||||
if (postTask != null) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user