clipboard implemented
This commit is contained in:
parent
cfac7e28ab
commit
b28b090fa9
BIN
kfmanager.png
BIN
kfmanager.png
Binary file not shown.
|
Before Width: | Height: | Size: 77 KiB |
130
src/main/java/cz/kamma/kfmanager/service/ClipboardService.java
Normal file
130
src/main/java/cz/kamma/kfmanager/service/ClipboardService.java
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
package cz.kamma.kfmanager.service;
|
||||||
|
|
||||||
|
import java.awt.Toolkit;
|
||||||
|
import java.awt.datatransfer.*;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ClipboardService {
|
||||||
|
|
||||||
|
public enum ClipboardAction {
|
||||||
|
COPY, CUT
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void copyToClipboard(List<File> files, ClipboardAction action) {
|
||||||
|
if (files == null || files.isEmpty()) return;
|
||||||
|
|
||||||
|
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
|
||||||
|
FileTransferable transferable = new FileTransferable(files, action);
|
||||||
|
clipboard.setContents(transferable, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static List<File> getFilesFromClipboard() {
|
||||||
|
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
|
||||||
|
if (clipboard.isDataFlavorAvailable(DataFlavor.javaFileListFlavor)) {
|
||||||
|
try {
|
||||||
|
return (List<File>) clipboard.getData(DataFlavor.javaFileListFlavor);
|
||||||
|
} catch (UnsupportedFlavorException | IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ClipboardAction getClipboardAction() {
|
||||||
|
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
|
||||||
|
|
||||||
|
// Check for GNOME/KDE flavored cut/copy
|
||||||
|
try {
|
||||||
|
for (DataFlavor flavor : clipboard.getAvailableDataFlavors()) {
|
||||||
|
if ("x-special/gnome-copied-files".equals(flavor.getSubType())) {
|
||||||
|
String data = (String) clipboard.getData(flavor);
|
||||||
|
if (data != null && data.startsWith("cut")) {
|
||||||
|
return ClipboardAction.CUT;
|
||||||
|
}
|
||||||
|
return ClipboardAction.COPY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception ignore) {}
|
||||||
|
|
||||||
|
// Check for Windows "Preferred DropEffect"
|
||||||
|
try {
|
||||||
|
for (DataFlavor flavor : clipboard.getAvailableDataFlavors()) {
|
||||||
|
if ("application/x-java-remote-object".equals(flavor.getPrimaryType()) &&
|
||||||
|
Integer.class.isAssignableFrom(flavor.getRepresentationClass()) &&
|
||||||
|
"Preferred DropEffect".equals(flavor.getHumanPresentableName())) {
|
||||||
|
Integer effect = (Integer) clipboard.getData(flavor);
|
||||||
|
if (effect != null && (effect & 2) != 0) { // 2 = MOVE
|
||||||
|
return ClipboardAction.CUT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception ignore) {}
|
||||||
|
|
||||||
|
// Internal fallback (if we set it ourselves)
|
||||||
|
if (clipboard.isDataFlavorAvailable(FileTransferable.ACTION_FLAVOR)) {
|
||||||
|
try {
|
||||||
|
return (ClipboardAction) clipboard.getData(FileTransferable.ACTION_FLAVOR);
|
||||||
|
} catch (UnsupportedFlavorException | IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ClipboardAction.COPY;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class FileTransferable implements Transferable {
|
||||||
|
public static final DataFlavor ACTION_FLAVOR = new DataFlavor(ClipboardAction.class, "Clipboard Action");
|
||||||
|
private final List<File> files;
|
||||||
|
private final ClipboardAction action;
|
||||||
|
|
||||||
|
public FileTransferable(List<File> files, ClipboardAction action) {
|
||||||
|
this.files = files;
|
||||||
|
this.action = action;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DataFlavor[] getTransferDataFlavors() {
|
||||||
|
try {
|
||||||
|
return new DataFlavor[]{
|
||||||
|
DataFlavor.javaFileListFlavor,
|
||||||
|
ACTION_FLAVOR,
|
||||||
|
new DataFlavor("x-special/gnome-copied-files;class=java.lang.String")
|
||||||
|
};
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
return new DataFlavor[]{
|
||||||
|
DataFlavor.javaFileListFlavor,
|
||||||
|
ACTION_FLAVOR
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isDataFlavorSupported(DataFlavor flavor) {
|
||||||
|
for (DataFlavor f : getTransferDataFlavors()) {
|
||||||
|
if (f.equals(flavor)) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
|
||||||
|
if (DataFlavor.javaFileListFlavor.equals(flavor)) {
|
||||||
|
return files;
|
||||||
|
} else if (ACTION_FLAVOR.equals(flavor)) {
|
||||||
|
return action;
|
||||||
|
} else if ("x-special".equals(flavor.getPrimaryType()) && "gnome-copied-files".equals(flavor.getSubType())) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append(action == ClipboardAction.CUT ? "cut" : "copy");
|
||||||
|
for (File f : files) {
|
||||||
|
sb.append("\n").append(f.toURI().toString());
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
throw new UnsupportedFlavorException(flavor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -30,6 +30,11 @@ public class FileOperations {
|
|||||||
if (callback != null && callback.isCancelled()) break;
|
if (callback != null && callback.isCancelled()) break;
|
||||||
File source = item.getFile();
|
File source = item.getFile();
|
||||||
File target = new File(targetDirectory, source.getName());
|
File target = new File(targetDirectory, source.getName());
|
||||||
|
|
||||||
|
// If target is the same as source (copying to the same directory), rename target
|
||||||
|
if (source.getAbsolutePath().equals(target.getAbsolutePath())) {
|
||||||
|
target = new File(targetDirectory, "copy-of-" + source.getName());
|
||||||
|
}
|
||||||
|
|
||||||
if (source.isDirectory()) {
|
if (source.isDirectory()) {
|
||||||
copyDirectory(source.toPath(), target.toPath(), totalSize, currentCopied, callback);
|
copyDirectory(source.toPath(), target.toPath(), totalSize, currentCopied, callback);
|
||||||
|
|||||||
@ -1,11 +1,15 @@
|
|||||||
package cz.kamma.kfmanager.ui;
|
package cz.kamma.kfmanager.ui;
|
||||||
|
|
||||||
import cz.kamma.kfmanager.model.FileItem;
|
import cz.kamma.kfmanager.model.FileItem;
|
||||||
|
import cz.kamma.kfmanager.service.ClipboardService;
|
||||||
|
import cz.kamma.kfmanager.service.FileOperations;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import javax.swing.table.AbstractTableModel;
|
import javax.swing.table.AbstractTableModel;
|
||||||
import javax.swing.table.DefaultTableCellRenderer;
|
import javax.swing.table.DefaultTableCellRenderer;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.awt.event.KeyEvent;
|
||||||
import java.awt.datatransfer.DataFlavor;
|
import java.awt.datatransfer.DataFlavor;
|
||||||
import java.awt.datatransfer.StringSelection;
|
import java.awt.datatransfer.StringSelection;
|
||||||
import java.awt.datatransfer.Transferable;
|
import java.awt.datatransfer.Transferable;
|
||||||
@ -836,15 +840,20 @@ public class FilePanelTab extends JPanel {
|
|||||||
fileTable.revalidate();
|
fileTable.revalidate();
|
||||||
fileTable.repaint();
|
fileTable.repaint();
|
||||||
|
|
||||||
if (selectFirst && fileTable.getRowCount() > 0) {
|
if (selectFirst && tableModel.items.size() > 0) {
|
||||||
fileTable.setRowSelectionInterval(0, 0);
|
int startIndex = 0;
|
||||||
fileTable.scrollRectToVisible(fileTable.getCellRect(0, 0, true));
|
int selRow = startIndex % tableModel.briefRowsPerColumn;
|
||||||
|
int selCol = startIndex / tableModel.briefRowsPerColumn;
|
||||||
|
briefCurrentColumn = selCol;
|
||||||
|
fileTable.setRowSelectionInterval(selRow, selRow);
|
||||||
|
fileTable.scrollRectToVisible(fileTable.getCellRect(selRow, selCol, true));
|
||||||
}
|
}
|
||||||
fileTable.requestFocusInWindow();
|
fileTable.requestFocusInWindow();
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
if (autoSelectFirst && fileTable.getRowCount() > 0) {
|
if (autoSelectFirst && fileTable.getRowCount() > 0) {
|
||||||
fileTable.setRowSelectionInterval(0, 0);
|
int startIndex = 0;
|
||||||
|
fileTable.setRowSelectionInterval(startIndex, startIndex);
|
||||||
SwingUtilities.invokeLater(() -> {
|
SwingUtilities.invokeLater(() -> {
|
||||||
try { fileTable.requestFocusInWindow(); } catch (Exception ignore) {}
|
try { fileTable.requestFocusInWindow(); } catch (Exception ignore) {}
|
||||||
});
|
});
|
||||||
@ -1256,6 +1265,28 @@ public class FilePanelTab extends JPanel {
|
|||||||
});
|
});
|
||||||
menu.add(copyPath);
|
menu.add(copyPath);
|
||||||
|
|
||||||
|
menu.addSeparator();
|
||||||
|
|
||||||
|
// Cut
|
||||||
|
JMenuItem cutItem = new JMenuItem("Cut");
|
||||||
|
cutItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_X, ActionEvent.CTRL_MASK));
|
||||||
|
cutItem.addActionListener(ae -> copyToClipboard(true));
|
||||||
|
menu.add(cutItem);
|
||||||
|
|
||||||
|
// Copy
|
||||||
|
JMenuItem copyItem = new JMenuItem("Copy");
|
||||||
|
copyItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C, ActionEvent.CTRL_MASK));
|
||||||
|
copyItem.addActionListener(ae -> copyToClipboard(false));
|
||||||
|
menu.add(copyItem);
|
||||||
|
|
||||||
|
// Paste
|
||||||
|
JMenuItem pasteItem = new JMenuItem("Paste");
|
||||||
|
pasteItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_V, ActionEvent.CTRL_MASK));
|
||||||
|
pasteItem.addActionListener(ae -> pasteFromClipboard());
|
||||||
|
menu.add(pasteItem);
|
||||||
|
|
||||||
|
menu.addSeparator();
|
||||||
|
|
||||||
// Associate with...
|
// Associate with...
|
||||||
JMenuItem associateItem = new JMenuItem("Associate with...");
|
JMenuItem associateItem = new JMenuItem("Associate with...");
|
||||||
associateItem.addActionListener(ae -> {
|
associateItem.addActionListener(ae -> {
|
||||||
@ -1275,6 +1306,7 @@ public class FilePanelTab extends JPanel {
|
|||||||
if (res == JOptionPane.YES_OPTION) {
|
if (res == JOptionPane.YES_OPTION) {
|
||||||
java.util.List<FileItem> toDelete = new java.util.ArrayList<>();
|
java.util.List<FileItem> toDelete = new java.util.ArrayList<>();
|
||||||
toDelete.add(item);
|
toDelete.add(item);
|
||||||
|
final int rememberedIndex = getFocusedItemIndex();
|
||||||
Window parentWindow = SwingUtilities.getWindowAncestor(FilePanelTab.this);
|
Window parentWindow = SwingUtilities.getWindowAncestor(FilePanelTab.this);
|
||||||
ProgressDialog progressDialog = new ProgressDialog(parentWindow instanceof Frame ? (Frame)parentWindow : null, "Deleting");
|
ProgressDialog progressDialog = new ProgressDialog(parentWindow instanceof Frame ? (Frame)parentWindow : null, "Deleting");
|
||||||
|
|
||||||
@ -1292,7 +1324,11 @@ public class FilePanelTab extends JPanel {
|
|||||||
});
|
});
|
||||||
SwingUtilities.invokeLater(() -> {
|
SwingUtilities.invokeLater(() -> {
|
||||||
progressDialog.dispose();
|
progressDialog.dispose();
|
||||||
loadDirectory(getCurrentDirectory());
|
loadDirectory(getCurrentDirectory(), false);
|
||||||
|
// Use another invokeLater to ensure BRIEF mode layout is updated
|
||||||
|
SwingUtilities.invokeLater(() -> {
|
||||||
|
selectItemByIndex(rememberedIndex - 1);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
SwingUtilities.invokeLater(() -> {
|
SwingUtilities.invokeLater(() -> {
|
||||||
@ -1473,6 +1509,118 @@ public class FilePanelTab extends JPanel {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getFocusedItemIndex() {
|
||||||
|
int row = fileTable.getSelectedRow();
|
||||||
|
if (row < 0) return -1;
|
||||||
|
if (viewMode == ViewMode.BRIEF) {
|
||||||
|
return briefCurrentColumn * tableModel.briefRowsPerColumn + row;
|
||||||
|
} else {
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void selectItemByIndex(int index) {
|
||||||
|
if (index < 0) index = 0;
|
||||||
|
if (index >= tableModel.items.size()) index = tableModel.items.size() - 1;
|
||||||
|
if (index < 0) return;
|
||||||
|
|
||||||
|
if (viewMode == ViewMode.BRIEF) {
|
||||||
|
briefCurrentColumn = index / tableModel.briefRowsPerColumn;
|
||||||
|
int row = index % tableModel.briefRowsPerColumn;
|
||||||
|
if (briefCurrentColumn < fileTable.getColumnCount()) {
|
||||||
|
fileTable.setRowSelectionInterval(row, row);
|
||||||
|
fileTable.scrollRectToVisible(fileTable.getCellRect(row, briefCurrentColumn, true));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fileTable.setRowSelectionInterval(index, index);
|
||||||
|
fileTable.scrollRectToVisible(fileTable.getCellRect(index, 0, true));
|
||||||
|
}
|
||||||
|
fileTable.repaint();
|
||||||
|
fileTable.requestFocusInWindow();
|
||||||
|
updateStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void copyToClipboard(boolean cut) {
|
||||||
|
List<FileItem> selected = getSelectedItems();
|
||||||
|
if (selected.isEmpty()) return;
|
||||||
|
|
||||||
|
List<File> files = new ArrayList<>();
|
||||||
|
for (FileItem item : selected) {
|
||||||
|
files.add(item.getFile());
|
||||||
|
}
|
||||||
|
|
||||||
|
ClipboardService.copyToClipboard(files,
|
||||||
|
cut ? ClipboardService.ClipboardAction.CUT : ClipboardService.ClipboardAction.COPY);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void pasteFromClipboard() {
|
||||||
|
List<File> files = ClipboardService.getFilesFromClipboard();
|
||||||
|
if (files == null || files.isEmpty()) return;
|
||||||
|
|
||||||
|
ClipboardService.ClipboardAction action = ClipboardService.getClipboardAction();
|
||||||
|
|
||||||
|
List<FileItem> itemsToPaste = new ArrayList<>();
|
||||||
|
for (File f : files) {
|
||||||
|
itemsToPaste.add(new FileItem(f));
|
||||||
|
}
|
||||||
|
|
||||||
|
File targetDir = getCurrentDirectory();
|
||||||
|
Window parentWindow = SwingUtilities.getWindowAncestor(this);
|
||||||
|
String operationName = action == ClipboardService.ClipboardAction.CUT ? "Moving" : "Copying";
|
||||||
|
ProgressDialog progressDialog = new ProgressDialog(parentWindow instanceof Frame ? (Frame)parentWindow : null, operationName);
|
||||||
|
|
||||||
|
new Thread(() -> {
|
||||||
|
try {
|
||||||
|
if (action == ClipboardService.ClipboardAction.CUT) {
|
||||||
|
FileOperations.move(itemsToPaste, targetDir, new FileOperations.ProgressCallback() {
|
||||||
|
@Override
|
||||||
|
public void onProgress(long current, long total, String currentFile) {
|
||||||
|
progressDialog.updateProgress(current, total, currentFile);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public boolean isCancelled() {
|
||||||
|
return progressDialog.isCancelled();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
FileOperations.copy(itemsToPaste, targetDir, new FileOperations.ProgressCallback() {
|
||||||
|
@Override
|
||||||
|
public void onProgress(long current, long total, String currentFile) {
|
||||||
|
progressDialog.updateProgress(current, total, currentFile);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public boolean isCancelled() {
|
||||||
|
return progressDialog.isCancelled();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
SwingUtilities.invokeLater(() -> {
|
||||||
|
progressDialog.dispose();
|
||||||
|
loadDirectory(targetDir, false);
|
||||||
|
if (!itemsToPaste.isEmpty()) {
|
||||||
|
String nameToSelect = itemsToPaste.get(0).getName();
|
||||||
|
File firstSource = itemsToPaste.get(0).getFile();
|
||||||
|
// Check if we were copying within the same directory - if so, it was renamed to copy-of-...
|
||||||
|
if (action == ClipboardService.ClipboardAction.COPY &&
|
||||||
|
firstSource.getParentFile() != null &&
|
||||||
|
firstSource.getParentFile().getAbsolutePath().equals(targetDir.getAbsolutePath())) {
|
||||||
|
nameToSelect = "copy-of-" + nameToSelect;
|
||||||
|
}
|
||||||
|
final String finalName = nameToSelect;
|
||||||
|
// Use invokeLater to ensure layout (especially BRIEF mode) is updated before selection
|
||||||
|
SwingUtilities.invokeLater(() -> selectItem(finalName));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (Exception ex) {
|
||||||
|
SwingUtilities.invokeLater(() -> {
|
||||||
|
progressDialog.dispose();
|
||||||
|
JOptionPane.showMessageDialog(this, "Paste failed: " + ex.getMessage());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
progressDialog.setVisible(true);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mark/Select the very last item in the list
|
* Mark/Select the very last item in the list
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -858,6 +858,38 @@ public class MainWindow extends JFrame {
|
|||||||
KeyStroke.getKeyStroke(KeyEvent.VK_W, InputEvent.CTRL_DOWN_MASK),
|
KeyStroke.getKeyStroke(KeyEvent.VK_W, InputEvent.CTRL_DOWN_MASK),
|
||||||
JComponent.WHEN_IN_FOCUSED_WINDOW);
|
JComponent.WHEN_IN_FOCUSED_WINDOW);
|
||||||
|
|
||||||
|
// Ctrl+` - Home directory
|
||||||
|
rootPane.registerKeyboardAction(e -> {
|
||||||
|
if (activePanel != null && activePanel.getCurrentTab() != null) {
|
||||||
|
activePanel.getCurrentTab().loadDirectory(new File(System.getProperty("user.home")));
|
||||||
|
}
|
||||||
|
}, KeyStroke.getKeyStroke(KeyEvent.VK_BACK_QUOTE, InputEvent.CTRL_DOWN_MASK),
|
||||||
|
JComponent.WHEN_IN_FOCUSED_WINDOW);
|
||||||
|
|
||||||
|
// Ctrl+C - Copy to clipboard
|
||||||
|
rootPane.registerKeyboardAction(e -> {
|
||||||
|
if (activePanel != null && activePanel.getCurrentTab() != null) {
|
||||||
|
activePanel.getCurrentTab().copyToClipboard(false);
|
||||||
|
}
|
||||||
|
}, KeyStroke.getKeyStroke(KeyEvent.VK_C, InputEvent.CTRL_DOWN_MASK),
|
||||||
|
JComponent.WHEN_IN_FOCUSED_WINDOW);
|
||||||
|
|
||||||
|
// Ctrl+X - Cut to clipboard
|
||||||
|
rootPane.registerKeyboardAction(e -> {
|
||||||
|
if (activePanel != null && activePanel.getCurrentTab() != null) {
|
||||||
|
activePanel.getCurrentTab().copyToClipboard(true);
|
||||||
|
}
|
||||||
|
}, KeyStroke.getKeyStroke(KeyEvent.VK_X, InputEvent.CTRL_DOWN_MASK),
|
||||||
|
JComponent.WHEN_IN_FOCUSED_WINDOW);
|
||||||
|
|
||||||
|
// Ctrl+V - Paste from clipboard
|
||||||
|
rootPane.registerKeyboardAction(e -> {
|
||||||
|
if (activePanel != null && activePanel.getCurrentTab() != null) {
|
||||||
|
activePanel.getCurrentTab().pasteFromClipboard();
|
||||||
|
}
|
||||||
|
}, KeyStroke.getKeyStroke(KeyEvent.VK_V, InputEvent.CTRL_DOWN_MASK),
|
||||||
|
JComponent.WHEN_IN_FOCUSED_WINDOW);
|
||||||
|
|
||||||
// Ctrl+E - Command line history
|
// Ctrl+E - Command line history
|
||||||
rootPane.registerKeyboardAction(e -> showCommandLineHistory(),
|
rootPane.registerKeyboardAction(e -> showCommandLineHistory(),
|
||||||
KeyStroke.getKeyStroke(KeyEvent.VK_E, InputEvent.CTRL_DOWN_MASK),
|
KeyStroke.getKeyStroke(KeyEvent.VK_E, InputEvent.CTRL_DOWN_MASK),
|
||||||
@ -981,6 +1013,40 @@ public class MainWindow extends JFrame {
|
|||||||
// Also map Shift+Delete on table level
|
// Also map Shift+Delete on table level
|
||||||
table.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
|
table.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
|
||||||
.put(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, InputEvent.SHIFT_DOWN_MASK), "deleteFiles");
|
.put(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, InputEvent.SHIFT_DOWN_MASK), "deleteFiles");
|
||||||
|
|
||||||
|
// Clipboard support (Ctrl+C, Ctrl+X, Ctrl+V)
|
||||||
|
table.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
|
||||||
|
.put(KeyStroke.getKeyStroke(KeyEvent.VK_C, InputEvent.CTRL_DOWN_MASK), "clipboardCopy");
|
||||||
|
table.getActionMap().put("clipboardCopy", new AbstractAction() {
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
if (activePanel != null && activePanel.getCurrentTab() != null) {
|
||||||
|
activePanel.getCurrentTab().copyToClipboard(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
table.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
|
||||||
|
.put(KeyStroke.getKeyStroke(KeyEvent.VK_X, InputEvent.CTRL_DOWN_MASK), "clipboardCut");
|
||||||
|
table.getActionMap().put("clipboardCut", new AbstractAction() {
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
if (activePanel != null && activePanel.getCurrentTab() != null) {
|
||||||
|
activePanel.getCurrentTab().copyToClipboard(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
table.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
|
||||||
|
.put(KeyStroke.getKeyStroke(KeyEvent.VK_V, InputEvent.CTRL_DOWN_MASK), "clipboardPaste");
|
||||||
|
table.getActionMap().put("clipboardPaste", new AbstractAction() {
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
if (activePanel != null && activePanel.getCurrentTab() != null) {
|
||||||
|
activePanel.getCurrentTab().pasteFromClipboard();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1011,8 +1077,9 @@ public class MainWindow extends JFrame {
|
|||||||
@Override
|
@Override
|
||||||
public void keyTyped(KeyEvent e) {
|
public void keyTyped(KeyEvent e) {
|
||||||
char c = e.getKeyChar();
|
char c = e.getKeyChar();
|
||||||
// Printable characters only (exclude control keys like Enter, Backspace, Esc, Tab)
|
// Transfer to command line only for printable characters and when no modifiers like Ctrl or Alt are pressed.
|
||||||
if (c != KeyEvent.CHAR_UNDEFINED && c != '\b' && c != '\n' && c != '\t' && c != 27) {
|
// This prevents focus jump on shortcuts like Ctrl+C, Ctrl+V, etc.
|
||||||
|
if (c != KeyEvent.CHAR_UNDEFINED && c >= 32 && c != 127 && !e.isControlDown() && !e.isAltDown()) {
|
||||||
commandLine.requestFocusInWindow();
|
commandLine.requestFocusInWindow();
|
||||||
String current = commandLine.getEditor().getItem().toString();
|
String current = commandLine.getEditor().getItem().toString();
|
||||||
commandLine.getEditor().setItem(current + c);
|
commandLine.getEditor().setItem(current + c);
|
||||||
@ -1118,9 +1185,9 @@ public class MainWindow extends JFrame {
|
|||||||
JOptionPane.INFORMATION_MESSAGE);
|
JOptionPane.INFORMATION_MESSAGE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// remember current selection row so we can restore selection after deletion
|
// remember current selection index so we can restore selection after deletion
|
||||||
JTable table = activePanel != null ? activePanel.getFileTable() : null;
|
final int rememberedIndex = (activePanel != null && activePanel.getCurrentTab() != null) ?
|
||||||
final int rememberedRow = (table != null) ? table.getSelectedRow() : -1;
|
activePanel.getCurrentTab().getFocusedItemIndex() : -1;
|
||||||
|
|
||||||
StringBuilder message = new StringBuilder("Really delete the following items?\n\n");
|
StringBuilder message = new StringBuilder("Really delete the following items?\n\n");
|
||||||
for (FileItem item : selectedItems) {
|
for (FileItem item : selectedItems) {
|
||||||
@ -1142,20 +1209,15 @@ public class MainWindow extends JFrame {
|
|||||||
FileOperations.delete(selectedItems, callback);
|
FileOperations.delete(selectedItems, callback);
|
||||||
}, "Delete completed", false, activePanel);
|
}, "Delete completed", false, activePanel);
|
||||||
|
|
||||||
// After deletion and refresh, restore selection: stay on same row if possible,
|
// After deletion and refresh, restore selection: move focus to the nearest higher item.
|
||||||
// otherwise move selection one row up.
|
|
||||||
SwingUtilities.invokeLater(() -> {
|
SwingUtilities.invokeLater(() -> {
|
||||||
try {
|
try {
|
||||||
JTable t = activePanel != null ? activePanel.getFileTable() : null;
|
if (activePanel != null && activePanel.getCurrentTab() != null) {
|
||||||
if (t == null) return;
|
// Use another invokeLater to ensure BRIEF mode layout is updated
|
||||||
int rowCount = t.getRowCount();
|
SwingUtilities.invokeLater(() -> {
|
||||||
if (rowCount == 0) return;
|
activePanel.getCurrentTab().selectItemByIndex(rememberedIndex - 1);
|
||||||
int targetRow = rememberedRow;
|
});
|
||||||
if (targetRow < 0) targetRow = 0;
|
}
|
||||||
if (targetRow >= rowCount) targetRow = rowCount - 1; // move up if needed
|
|
||||||
t.setRowSelectionInterval(targetRow, targetRow);
|
|
||||||
t.scrollRectToVisible(t.getCellRect(targetRow, 0, true));
|
|
||||||
t.requestFocusInWindow();
|
|
||||||
} catch (Exception ignore) {}
|
} catch (Exception ignore) {}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.3 MiB After Width: | Height: | Size: 1.7 MiB |
Loading…
x
Reference in New Issue
Block a user