1
0
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:
X 2023-12-09 08:19:36 -05:00 committed by GitHub
parent 658241970d
commit 2e5ef70fbd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 368 additions and 25 deletions

View File

@ -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"))
}
}

View File

@ -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) {

View File

@ -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>");

View File

@ -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();
}
}
}

View File

@ -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());

View 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.");
}
}
}