mirror of
https://github.com/RipMeApp/ripme.git
synced 2025-01-17 04:38:18 +01:00
Patch/Bug fix for ContextMenuMouseListener Actions (#161)
* Should fix the issue associated with copy/paste. * Fix build gradle, and also create junit.
This commit is contained in:
parent
658241970d
commit
2e5ef70fbd
@ -135,12 +135,14 @@ tasks.withType<AbstractArchiveTask>().configureEach {
|
||||
isReproducibleFileOrder = true
|
||||
}
|
||||
|
||||
println("Build directory: ${file(layout.buildDirectory)}")
|
||||
|
||||
tasks.jacocoTestReport {
|
||||
dependsOn(tasks.test) // tests are required to run before generating the report
|
||||
reports {
|
||||
xml.required.set(false)
|
||||
csv.required.set(false)
|
||||
html.outputLocation.set(file("${layout.buildDirectory}/jacocoHtml"))
|
||||
html.outputLocation.set(file("${file(layout.buildDirectory)}/jacocoHtml"))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,15 +1,16 @@
|
||||
package com.rarchives.ripme.ui;
|
||||
|
||||
import java.awt.Toolkit;
|
||||
import java.awt.datatransfer.DataFlavor;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.InputEvent;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import com.rarchives.ripme.uiUtils.ContextActionProtections;
|
||||
|
||||
import javax.swing.AbstractAction;
|
||||
import javax.swing.Action;
|
||||
import javax.swing.JPopupMenu;
|
||||
import java.awt.Toolkit;
|
||||
import java.awt.datatransfer.Clipboard;
|
||||
import java.awt.datatransfer.DataFlavor;
|
||||
import java.awt.datatransfer.Transferable;
|
||||
import java.awt.datatransfer.UnsupportedFlavorException;
|
||||
import java.awt.event.*;
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.text.JTextComponent;
|
||||
|
||||
/**
|
||||
@ -20,27 +21,72 @@ import javax.swing.text.JTextComponent;
|
||||
public class ContextMenuMouseListener extends MouseAdapter {
|
||||
private JPopupMenu popup = new JPopupMenu();
|
||||
|
||||
public String getDebugSavedString() {
|
||||
return debugSavedString;
|
||||
}
|
||||
|
||||
private String debugSavedString;
|
||||
|
||||
public Action getCutAction() {
|
||||
return cutAction;
|
||||
}
|
||||
|
||||
private Action cutAction;
|
||||
private Action copyAction;
|
||||
private Action pasteAction;
|
||||
|
||||
public Action getCopyAction() {
|
||||
return copyAction;
|
||||
}
|
||||
|
||||
public Action getPasteAction() {
|
||||
return pasteAction;
|
||||
}
|
||||
|
||||
public Action getUndoAction() {
|
||||
return undoAction;
|
||||
}
|
||||
|
||||
public Action getSelectAllAction() {
|
||||
return selectAllAction;
|
||||
}
|
||||
|
||||
private Action undoAction;
|
||||
private Action selectAllAction;
|
||||
|
||||
public JTextComponent getTextComponent() {
|
||||
return textComponent;
|
||||
}
|
||||
|
||||
private JTextComponent textComponent;
|
||||
|
||||
public String getSavedString() {
|
||||
return savedString;
|
||||
}
|
||||
|
||||
private String savedString = "";
|
||||
private Actions lastActionSelected;
|
||||
|
||||
private enum Actions { UNDO, CUT, COPY, PASTE, SELECT_ALL }
|
||||
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public ContextMenuMouseListener() {
|
||||
public ContextMenuMouseListener(JTextField ripTextfield) {
|
||||
this.textComponent = ripTextfield;
|
||||
|
||||
//Add protection for cntl+v
|
||||
|
||||
generate_popup();
|
||||
}
|
||||
|
||||
private void generate_popup() {
|
||||
undoAction = new AbstractAction("Undo") {
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent ae) {
|
||||
textComponent.setText("");
|
||||
textComponent.replaceSelection(savedString);
|
||||
|
||||
debugSavedString = textComponent.getText();
|
||||
lastActionSelected = Actions.UNDO;
|
||||
}
|
||||
};
|
||||
@ -54,6 +100,7 @@ public class ContextMenuMouseListener extends MouseAdapter {
|
||||
public void actionPerformed(ActionEvent ae) {
|
||||
lastActionSelected = Actions.CUT;
|
||||
savedString = textComponent.getText();
|
||||
debugSavedString = savedString;
|
||||
textComponent.cut();
|
||||
}
|
||||
};
|
||||
@ -65,6 +112,7 @@ public class ContextMenuMouseListener extends MouseAdapter {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent ae) {
|
||||
lastActionSelected = Actions.COPY;
|
||||
debugSavedString = textComponent.getText();
|
||||
textComponent.copy();
|
||||
}
|
||||
};
|
||||
@ -77,7 +125,8 @@ public class ContextMenuMouseListener extends MouseAdapter {
|
||||
public void actionPerformed(ActionEvent ae) {
|
||||
lastActionSelected = Actions.PASTE;
|
||||
savedString = textComponent.getText();
|
||||
textComponent.paste();
|
||||
debugSavedString = savedString;
|
||||
ContextActionProtections.pasteFromClipboard(textComponent);
|
||||
}
|
||||
};
|
||||
|
||||
@ -89,6 +138,7 @@ public class ContextMenuMouseListener extends MouseAdapter {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent ae) {
|
||||
lastActionSelected = Actions.SELECT_ALL;
|
||||
debugSavedString = textComponent.getText();
|
||||
textComponent.selectAll();
|
||||
}
|
||||
};
|
||||
@ -96,6 +146,27 @@ public class ContextMenuMouseListener extends MouseAdapter {
|
||||
popup.add(selectAllAction);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void mousePressed(MouseEvent e) {
|
||||
showPopup(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseReleased(MouseEvent e) {
|
||||
showPopup(e);
|
||||
}
|
||||
|
||||
private void showPopup(MouseEvent e) {
|
||||
if (e.isPopupTrigger()) {
|
||||
if(this.popup == null) {
|
||||
popup = new JPopupMenu();
|
||||
generate_popup();
|
||||
}
|
||||
popup.show(e.getComponent(), e.getX(), e.getY());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
if (e.getModifiersEx() == InputEvent.BUTTON3_DOWN_MASK) {
|
||||
|
@ -1,6 +1,7 @@
|
||||
package com.rarchives.ripme.ui;
|
||||
|
||||
import com.rarchives.ripme.ripper.AbstractRipper;
|
||||
import com.rarchives.ripme.uiUtils.ContextActionProtections;
|
||||
import com.rarchives.ripme.utils.RipUtils;
|
||||
import com.rarchives.ripme.utils.Utils;
|
||||
import org.apache.logging.log4j.Level;
|
||||
@ -18,18 +19,10 @@ import javax.swing.event.DocumentListener;
|
||||
import javax.swing.event.ListDataEvent;
|
||||
import javax.swing.event.ListDataListener;
|
||||
import javax.swing.table.AbstractTableModel;
|
||||
import javax.swing.text.BadLocationException;
|
||||
import javax.swing.text.SimpleAttributeSet;
|
||||
import javax.swing.text.StyleConstants;
|
||||
import javax.swing.text.StyledDocument;
|
||||
import javax.swing.text.*;
|
||||
import java.awt.*;
|
||||
import java.awt.TrayIcon.MessageType;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.WindowAdapter;
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.awt.event.*;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
@ -57,6 +50,7 @@ public final class MainWindow implements Runnable, RipStatusHandler {
|
||||
private boolean isRipping = false; // Flag to indicate if we're ripping something
|
||||
|
||||
private static JFrame mainFrame;
|
||||
|
||||
private static JTextField ripTextfield;
|
||||
private static JButton ripButton, stopButton;
|
||||
|
||||
@ -281,7 +275,47 @@ public final class MainWindow implements Runnable, RipStatusHandler {
|
||||
}
|
||||
|
||||
ripTextfield = new JTextField("", 20);
|
||||
ripTextfield.addMouseListener(new ContextMenuMouseListener());
|
||||
ripTextfield.addMouseListener(new ContextMenuMouseListener(ripTextfield));
|
||||
|
||||
//Add keyboard protection of cntl + v for pasting.
|
||||
ripTextfield.addKeyListener(new KeyAdapter() {
|
||||
@Override
|
||||
public void keyTyped(KeyEvent e) {
|
||||
if (e.getKeyChar() == 22) { // ASCII code for Ctrl+V
|
||||
ContextActionProtections.pasteFromClipboard(ripTextfield);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
Alternatively, just set this, and use
|
||||
((AbstractDocument) ripTextfield.getDocument()).setDocumentFilter(new LengthLimitDocumentFilter(256));
|
||||
private static class LengthLimitDocumentFilter extends DocumentFilter {
|
||||
private final int maxLength;
|
||||
|
||||
public LengthLimitDocumentFilter(int maxLength) {
|
||||
this.maxLength = maxLength;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertString(FilterBypass fb, int offset, String string, AttributeSet attr) throws BadLocationException {
|
||||
// if ((fb.getDocument().getLength() + string.length()) <= maxLength) {
|
||||
super.insertString(fb, offset, string.substring(0, maxLength), attr);
|
||||
// }
|
||||
}
|
||||
|
||||
@Override
|
||||
public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs) throws BadLocationException {
|
||||
int currentLength = fb.getDocument().getLength();
|
||||
int newLength = currentLength - length + text.length();
|
||||
|
||||
// if (newLength <= maxLength) {
|
||||
super.replace(fb, offset, length, text.substring(0, maxLength), attrs);
|
||||
// }
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
ImageIcon ripIcon = new ImageIcon(mainIcon);
|
||||
ripButton = new JButton("<html><font size=\"5\"><b>Rip</b></font></html>", ripIcon);
|
||||
stopButton = new JButton("<html><font size=\"5\"><b>Stop</b></font></html>");
|
||||
|
@ -0,0 +1,30 @@
|
||||
package com.rarchives.ripme.uiUtils;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.text.JTextComponent;
|
||||
import java.awt.*;
|
||||
import java.awt.datatransfer.Clipboard;
|
||||
import java.awt.datatransfer.DataFlavor;
|
||||
import java.awt.datatransfer.Transferable;
|
||||
import java.awt.datatransfer.UnsupportedFlavorException;
|
||||
import java.io.IOException;
|
||||
|
||||
public class ContextActionProtections {
|
||||
public static void pasteFromClipboard(JTextComponent textComponent) {
|
||||
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
|
||||
Transferable transferable = clipboard.getContents(new Object());
|
||||
|
||||
try {
|
||||
String clipboardContent = (String) transferable.getTransferData(DataFlavor.stringFlavor);
|
||||
|
||||
// Limit the pasted content to 96 characters
|
||||
if (clipboardContent.length() > 96) {
|
||||
clipboardContent = clipboardContent.substring(0, 96);
|
||||
}
|
||||
// Set the text in the JTextField
|
||||
textComponent.setText(clipboardContent);
|
||||
} catch (UnsupportedFlavorException | IOException unable_to_modify_text_on_paste) {
|
||||
unable_to_modify_text_on_paste.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
@ -7,6 +7,10 @@ import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import com.rarchives.ripme.ripper.rippers.ChanRipper;
|
||||
import com.rarchives.ripme.ripper.rippers.ripperhelpers.ChanSite;
|
||||
@ -27,7 +31,22 @@ public class ChanRipperTest extends RippersTest {
|
||||
passURLs.add(new URI("https://rbt.asia/g/thread/70643087/").toURL()); //must work with TLDs with len of 4
|
||||
for (URL url : passURLs) {
|
||||
ChanRipper ripper = new ChanRipper(url);
|
||||
ripper.setup();
|
||||
// Use CompletableFuture to run setup() asynchronously
|
||||
CompletableFuture<Void> setupFuture = CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
ripper.setup();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
// Wait for up to 5 seconds for setup() to complete
|
||||
setupFuture.get(5, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException | ExecutionException |
|
||||
TimeoutException e) {
|
||||
e.printStackTrace(); // Handle exceptions as needed
|
||||
}
|
||||
assert (ripper.canRip(url));
|
||||
Assertions.assertNotNull(ripper.getWorkingDir(), "Ripper for " + url + " did not have a valid working directory.");
|
||||
deleteDir(ripper.getWorkingDir());
|
||||
|
187
src/test/java/com/rarchives/ripme/ui/UIContextMenuTests.java
Normal file
187
src/test/java/com/rarchives/ripme/ui/UIContextMenuTests.java
Normal file
@ -0,0 +1,187 @@
|
||||
package com.rarchives.ripme.ui;
|
||||
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.datatransfer.Clipboard;
|
||||
import java.awt.datatransfer.StringSelection;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
public class UIContextMenuTests {
|
||||
|
||||
private JFrame frame;
|
||||
private JTextField textField;
|
||||
private ContextMenuMouseListener contextMenuMouseListener;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() throws InterruptedException, InvocationTargetException {
|
||||
AtomicBoolean notDone = new AtomicBoolean(true);
|
||||
|
||||
SwingUtilities.invokeAndWait(() -> {
|
||||
frame = new JFrame("ContextMenuMouseListener Example");
|
||||
textField = new JTextField("Hello, world!");
|
||||
|
||||
// Create an instance of ContextMenuMouseListener
|
||||
contextMenuMouseListener = new ContextMenuMouseListener(textField);
|
||||
|
||||
// Add ContextMenuMouseListener to JTextField
|
||||
textField.addMouseListener(contextMenuMouseListener);
|
||||
|
||||
frame.getContentPane().add(textField, BorderLayout.CENTER);
|
||||
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
frame.setSize(300, 200);
|
||||
frame.setVisible(true);
|
||||
|
||||
notDone.set(false);
|
||||
});
|
||||
|
||||
// Wait for the GUI to be fully initialized
|
||||
while (notDone.get()) {
|
||||
Thread.yield();
|
||||
}
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
frame.dispose();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCut() {
|
||||
// Simulate a cut event
|
||||
simulateCutEvent();
|
||||
// Add assertions if needed
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCopy() {
|
||||
// Simulate a copy event
|
||||
simulateCopyEvent();
|
||||
// Add assertions if needed
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPaste() {
|
||||
// Simulate a paste event
|
||||
simulatePasteEvent();
|
||||
// Add assertions if needed
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSelectAll() {
|
||||
// Simulate a select all event
|
||||
simulateSelectAllEvent();
|
||||
// Add assertions if needed
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUndo() {
|
||||
// Simulate an undo event
|
||||
simulateUndoEvent();
|
||||
// Add assertions if needed
|
||||
}
|
||||
|
||||
private void simulatePasteEvent() {
|
||||
// Save the initial text content
|
||||
String initialText = contextMenuMouseListener.getTextComponent().getText();
|
||||
|
||||
// Assume there is some text to paste
|
||||
String textToPaste = "Text to paste";
|
||||
|
||||
// Set the text to the clipboard
|
||||
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
|
||||
StringSelection stringSelection = new StringSelection(textToPaste);
|
||||
clipboard.setContents(stringSelection, stringSelection);
|
||||
|
||||
// Simulate a paste event
|
||||
contextMenuMouseListener.getTextComponent().paste();
|
||||
|
||||
// Verify that the paste operation worked
|
||||
String actualText = contextMenuMouseListener.getTextComponent().getText();
|
||||
|
||||
// Check if the text was appended after the initial text
|
||||
if (actualText.equals(initialText + textToPaste)) {
|
||||
System.out.println("Paste operation successful. Text content matches.");
|
||||
} else {
|
||||
fail("Paste operation failed. Text content does not match.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private void simulateSelectAllEvent() {
|
||||
// Simulate a select all event by invoking the selectAllAction
|
||||
contextMenuMouseListener.getSelectAllAction().actionPerformed(new ActionEvent(contextMenuMouseListener.getTextComponent(), ActionEvent.ACTION_PERFORMED, ""));
|
||||
|
||||
// Verify that all text is selected
|
||||
int expectedSelectionStart = 0;
|
||||
int expectedSelectionEnd = contextMenuMouseListener.getTextComponent().getText().length();
|
||||
int actualSelectionStart = contextMenuMouseListener.getTextComponent().getSelectionStart();
|
||||
int actualSelectionEnd = contextMenuMouseListener.getTextComponent().getSelectionEnd();
|
||||
|
||||
if (expectedSelectionStart == actualSelectionStart && expectedSelectionEnd == actualSelectionEnd) {
|
||||
System.out.println("Select All operation successful. Text is selected.");
|
||||
} else {
|
||||
fail("Select All operation failed. Text is not selected as expected.");
|
||||
}
|
||||
}
|
||||
|
||||
private void simulateUndoEvent() {
|
||||
|
||||
// Simulate an undo event by invoking the undoAction
|
||||
contextMenuMouseListener.getUndoAction().actionPerformed(new ActionEvent(contextMenuMouseListener.getTextComponent(), ActionEvent.ACTION_PERFORMED, ""));
|
||||
|
||||
// Verify that the undo operation worked
|
||||
String expectedText = contextMenuMouseListener.getSavedString(); // Assuming the undo reverts to the saved state
|
||||
String actualText = contextMenuMouseListener.getTextComponent().getText();
|
||||
|
||||
if (expectedText.equals(actualText)) {
|
||||
System.out.println("Undo operation successful. Text content matches.");
|
||||
} else {
|
||||
fail("Undo operation failed. Text content does not match.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void simulateCopyEvent() {
|
||||
// Save the initial text content
|
||||
String initialText = contextMenuMouseListener.getTextComponent().getText();
|
||||
|
||||
// Simulate a copy event by invoking the copyAction
|
||||
contextMenuMouseListener.getCopyAction().actionPerformed(new ActionEvent(contextMenuMouseListener.getTextComponent(), ActionEvent.ACTION_PERFORMED, ""));
|
||||
|
||||
// Verify that the copy operation worked
|
||||
String actualText = contextMenuMouseListener.getDebugSavedString();
|
||||
|
||||
if (initialText.equals(actualText)) {
|
||||
System.out.println("Copy operation successful. Text content matches.");
|
||||
} else {
|
||||
fail("Copy operation failed. Text content does not match.");
|
||||
}
|
||||
}
|
||||
|
||||
private void simulateCutEvent() {
|
||||
// Save the initial text content
|
||||
String initialText = contextMenuMouseListener.getTextComponent().getText();
|
||||
|
||||
// Simulate a cut event by invoking the cutAction
|
||||
contextMenuMouseListener.getCutAction().actionPerformed(new ActionEvent(contextMenuMouseListener.getTextComponent(), ActionEvent.ACTION_PERFORMED, ""));
|
||||
|
||||
// Verify that the cut operation worked
|
||||
String actualText = contextMenuMouseListener.getDebugSavedString();
|
||||
|
||||
if (initialText.equals(actualText)) {
|
||||
System.out.println("Cut operation successful. Text content matches.");
|
||||
} else {
|
||||
fail("Cut operation failed. Text content does not match.");
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user