From 6f626b7607aa7b0318a73853c7c0bd7ad728117d Mon Sep 17 00:00:00 2001 From: rdavidek Date: Wed, 14 Jan 2026 21:12:07 +0100 Subject: [PATCH] added undo/redu, clipboard --- .../java/com/kfmanager/ui/FileEditor.java | 191 +++++++++++++++++- 1 file changed, 186 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/kfmanager/ui/FileEditor.java b/src/main/java/com/kfmanager/ui/FileEditor.java index ccdc88e..283d001 100644 --- a/src/main/java/com/kfmanager/ui/FileEditor.java +++ b/src/main/java/com/kfmanager/ui/FileEditor.java @@ -38,6 +38,7 @@ public class FileEditor extends JDialog { private JButton prevPageBtn = null; private JButton nextPageBtn = null; private JLabel pageOffsetLabel = null; + private javax.swing.undo.UndoManager undoManager; public FileEditor(Window parent, File file, AppConfig config, boolean readOnly) { super(parent, (readOnly ? "Prohlížeč - " : "Editor - ") + file.getName(), ModalityType.MODELESS); @@ -72,11 +73,11 @@ public class FileEditor extends JDialog { textArea.setTabSize(4); textArea.setEditable(!readOnly); - // V read-only režimu zajistit viditelnost kurzoru a možnost označování - if (readOnly) { - textArea.getCaret().setVisible(true); - textArea.setCaretColor(Color.BLACK); - } + // Undo/Redo support + undoManager = new javax.swing.undo.UndoManager(); + textArea.getDocument().addUndoableEditListener(e -> { + undoManager.addEdit(e.getEdit()); + }); // Sledování změn (pouze pro editovatelný režim) if (!readOnly) { @@ -98,6 +99,12 @@ public class FileEditor extends JDialog { statusPanel.add(statusPosLabel, BorderLayout.WEST); statusPanel.add(statusSelLabel, BorderLayout.EAST); add(statusPanel, BorderLayout.SOUTH); + + // Apply appearance settings (colors, contrast caret etc) + applyAppearance(); + + // Kontextové menu pro clipboard + setupContextMenu(); // Klávesové zkratky setupKeyBindings(); @@ -112,6 +119,109 @@ public class FileEditor extends JDialog { }); } + private void setupContextMenu() { + JPopupMenu popup = new JPopupMenu(); + + JMenuItem undoItem = new JMenuItem("Zpět"); + undoItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Z, InputEvent.CTRL_DOWN_MASK)); + undoItem.addActionListener(e -> { if (undoManager.canUndo()) undoManager.undo(); }); + + JMenuItem redoItem = new JMenuItem("Znovu"); + redoItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Y, InputEvent.CTRL_DOWN_MASK)); + redoItem.addActionListener(e -> { if (undoManager.canRedo()) undoManager.redo(); }); + + popup.add(undoItem); + popup.add(redoItem); + popup.addSeparator(); + + JMenuItem cutItem = new JMenuItem(new javax.swing.text.DefaultEditorKit.CutAction()); + cutItem.setText("Vyjmout"); + cutItem.setMnemonic(KeyEvent.VK_X); + + JMenuItem copyItem = new JMenuItem(new javax.swing.text.DefaultEditorKit.CopyAction()); + copyItem.setText("Kopírovat"); + copyItem.setMnemonic(KeyEvent.VK_C); + + JMenuItem pasteItem = new JMenuItem(new javax.swing.text.DefaultEditorKit.PasteAction()); + pasteItem.setText("Vložit"); + pasteItem.setMnemonic(KeyEvent.VK_V); + + popup.add(cutItem); + popup.add(copyItem); + popup.add(pasteItem); + popup.addSeparator(); + + JMenuItem selectAllItem = new JMenuItem("Vybrat vše"); + selectAllItem.setMnemonic(KeyEvent.VK_A); + selectAllItem.addActionListener(e -> textArea.selectAll()); + popup.add(selectAllItem); + + popup.addPopupMenuListener(new javax.swing.event.PopupMenuListener() { + @Override + public void popupMenuWillBecomeVisible(javax.swing.event.PopupMenuEvent e) { + boolean editable = textArea.isEditable(); + undoItem.setEnabled(editable && undoManager.canUndo()); + redoItem.setEnabled(editable && undoManager.canRedo()); + cutItem.setEnabled(editable); + pasteItem.setEnabled(editable); + } + @Override public void popupMenuWillBecomeInvisible(javax.swing.event.PopupMenuEvent e) {} + @Override public void popupMenuCanceled(javax.swing.event.PopupMenuEvent e) {} + }); + + textArea.setComponentPopupMenu(popup); + } + + private void applyAppearance() { + Color bg = config.getBackgroundColor(); + if (bg != null) { + boolean dark = isDark(bg); + // Apply to JTextArea + textArea.setBackground(bg); + textArea.setForeground(dark ? Color.WHITE : Color.BLACK); + textArea.setCaretColor(dark ? Color.WHITE : Color.BLACK); + + // Apply to the rest of the components recursively + applyRecursiveColors(getContentPane(), bg, dark); + } else { + // Default colors - make sure caret is at least visible + Color defaultBg = textArea.getBackground(); + if (defaultBg != null) { + boolean dark = isDark(defaultBg); + textArea.setCaretColor(dark ? Color.WHITE : Color.BLACK); + } else { + textArea.setCaretColor(Color.BLACK); + } + } + + // Force caret visibility in read-only mode + if (readOnly) { + textArea.getCaret().setVisible(true); + } + } + + private void applyRecursiveColors(Container container, Color bg, boolean dark) { + if (container == null) return; + container.setBackground(bg); + for (Component c : container.getComponents()) { + if (c instanceof JPanel || c instanceof JScrollPane || c instanceof JViewport) { + c.setBackground(bg); + } + if (c instanceof JLabel) { + c.setForeground(dark ? Color.WHITE : Color.BLACK); + } + if (c instanceof Container) { + applyRecursiveColors((Container) c, bg, dark); + } + } + } + + private boolean isDark(Color c) { + if (c == null) return false; + double darkness = 1 - (0.299 * c.getRed() + 0.587 * c.getGreen() + 0.114 * c.getBlue()) / 255; + return darkness >= 0.5; + } + private void createMenuBar() { JMenuBar menuBar = new JMenuBar(); @@ -136,6 +246,60 @@ public class FileEditor extends JDialog { menuBar.add(fileMenu); + // Menu Edit + JMenu editMenu = new JMenu("Edit"); + editMenu.setMnemonic(java.awt.event.KeyEvent.VK_E); + + JMenuItem undoItem = new JMenuItem("Zpět"); + 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"); + redoItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Y, InputEvent.CTRL_DOWN_MASK)); + redoItem.addActionListener(e -> { if (undoManager.canRedo()) undoManager.redo(); }); + editMenu.add(redoItem); + + editMenu.addSeparator(); + + JMenuItem cutItem = new JMenuItem(new javax.swing.text.DefaultEditorKit.CutAction()); + cutItem.setText("Vyjmout"); + 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.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.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_V, InputEvent.CTRL_DOWN_MASK)); + editMenu.add(pasteItem); + + editMenu.addSeparator(); + + JMenuItem selectAllItem = new JMenuItem("Vybrat vše"); + selectAllItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_A, InputEvent.CTRL_DOWN_MASK)); + selectAllItem.addActionListener(e -> textArea.selectAll()); + editMenu.add(selectAllItem); + + // Update menu items state when menu is selected + editMenu.addMenuListener(new javax.swing.event.MenuListener() { + @Override + public void menuSelected(javax.swing.event.MenuEvent e) { + boolean editable = textArea.isEditable(); + undoItem.setEnabled(editable && undoManager.canUndo()); + redoItem.setEnabled(editable && undoManager.canRedo()); + cutItem.setEnabled(editable); + pasteItem.setEnabled(editable); + } + @Override public void menuDeselected(javax.swing.event.MenuEvent e) {} + @Override public void menuCanceled(javax.swing.event.MenuEvent e) {} + }); + + menuBar.add(editMenu); + // Menu Nastavení JMenu settingsMenu = new JMenu("Nastavení"); settingsMenu.setMnemonic(java.awt.event.KeyEvent.VK_N); @@ -215,6 +379,21 @@ public class FileEditor extends JDialog { }, KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN, 0), JComponent.WHEN_IN_FOCUSED_WINDOW); + + // Ctrl+Z - Undo + rootPane.registerKeyboardAction(e -> { + if (undoManager.canUndo()) undoManager.undo(); + }, KeyStroke.getKeyStroke(KeyEvent.VK_Z, InputEvent.CTRL_DOWN_MASK), JComponent.WHEN_IN_FOCUSED_WINDOW); + + // Ctrl+Y - Redo + rootPane.registerKeyboardAction(e -> { + if (undoManager.canRedo()) undoManager.redo(); + }, KeyStroke.getKeyStroke(KeyEvent.VK_Y, InputEvent.CTRL_DOWN_MASK), JComponent.WHEN_IN_FOCUSED_WINDOW); + + // Ctrl+Shift+Z - Redo (alternative shortcut) + rootPane.registerKeyboardAction(e -> { + if (undoManager.canRedo()) undoManager.redo(); + }, KeyStroke.getKeyStroke(KeyEvent.VK_Z, InputEvent.CTRL_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK), JComponent.WHEN_IN_FOCUSED_WINDOW); } private void setHexMode(boolean on) { @@ -256,6 +435,7 @@ public class FileEditor extends JDialog { textArea.setEditable(!readOnly); textArea.setFont(config.getEditorFont()); } + applyAppearance(); updateStatus(); } @@ -442,6 +622,7 @@ public class FileEditor extends JDialog { textArea.setCaretPosition(0); } } + if (undoManager != null) undoManager.discardAllEdits(); modified = false; updateTitle(); updateStatus();