Added: Copy (selectable) text to clipboard, select all

No text selection in object selection mode.
This commit is contained in:
Jindra Petřík
2025-07-06 15:34:45 +02:00
parent 07984d5154
commit 8d0952a8fc
5 changed files with 122 additions and 4 deletions

View File

@@ -8,7 +8,7 @@ All notable changes to this project will be documented in this file.
and shows its progress
- AS2 - show deobfuscated class/package names in the class tree
- Allow obfuscated DefineEditText variable identifiers
- Selectable text (DefineEditTexts with noselect=0)
- Selectable text (DefineEditTexts with noselect=0), copy to clipboard, select all
- AS1/2 P-code curly braces pair highlighting
### Fixed

View File

@@ -43,6 +43,7 @@ import com.jpexs.decompiler.flash.tags.base.ButtonTag;
import com.jpexs.decompiler.flash.tags.base.CharacterTag;
import com.jpexs.decompiler.flash.tags.base.DisplayObjectCacheKey;
import com.jpexs.decompiler.flash.tags.base.DrawableTag;
import com.jpexs.decompiler.flash.tags.base.FontTag;
import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag;
import com.jpexs.decompiler.flash.tags.base.RenderContext;
import com.jpexs.decompiler.flash.tags.base.SoundTag;
@@ -54,6 +55,7 @@ import com.jpexs.decompiler.flash.timeline.Timeline;
import com.jpexs.decompiler.flash.timeline.Timelined;
import com.jpexs.decompiler.flash.types.BUTTONCONDACTION;
import com.jpexs.decompiler.flash.types.ConstantColorColorTransform;
import com.jpexs.decompiler.flash.types.GLYPHENTRY;
import com.jpexs.decompiler.flash.types.RECT;
import com.jpexs.decompiler.flash.types.RGB;
import com.jpexs.decompiler.flash.types.SOUNDINFO;
@@ -87,6 +89,8 @@ import java.awt.Stroke;
import java.awt.SystemColor;
import java.awt.Toolkit;
import java.awt.Transparency;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.AdjustmentEvent;
@@ -127,7 +131,9 @@ import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.UnsupportedAudioFileException;
import javax.swing.BoxLayout;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollBar;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
@@ -416,6 +422,12 @@ public final class ImagePanel extends JPanel implements MediaDisplay {
private Timer textCursorBlinkTimer;
private void setTextSelection(int value) {
int selStart = getSelectionStartInt();
int delta = value - selStart;
changeTextSelection(delta);
}
private void changeTextSelection(int delta) {
TextTag text = textSelectionText;
if (text == null) {
@@ -1646,6 +1658,78 @@ public final class ImagePanel extends JPanel implements MediaDisplay {
@Override
public void mouseClicked(MouseEvent e) {
if (SwingUtilities.isRightMouseButton(e) && !selectionMode && !doFreeTransform) {
TextTag text = mouseOverText;
if (text != null) {
if (text instanceof DefineEditTextTag) {
DefineEditTextTag dtext = (DefineEditTextTag) text;
List<TEXTRECORD> recs = dtext.getTextRecords(dtext.getSwf());
FontTag font = null;
int pos = 0;
int selStart = getSelectionStartInt();
int selEnd = getSelectionEndInt();
StringBuilder sb = new StringBuilder();
int y = 0;
for (TEXTRECORD r : recs) {
if (r.styleFlagsHasFont) {
font = r.getFont(dtext.getSwf());
}
if (r.styleFlagsHasYOffset) {
int oldY = y;
y = r.yOffset;
if (text == textSelectionText && pos >= selStart && pos < selEnd) {
if (y > oldY && sb.length() > 0) {
sb.append("\n");
}
}
}
if (font == null) {
continue;
}
for (GLYPHENTRY g : r.glyphEntries) {
if (text == textSelectionText && pos >= selStart && pos < selEnd) {
sb.append(font.glyphToChar(g.glyphIndex));
}
pos++;
}
}
String textToCopy = sb.toString();
int fullLength = pos;
JPopupMenu pm = new JPopupMenu();
JMenuItem copyMenuItem = new JMenuItem(AppStrings.translate("text.copy"), View.getIcon("copy16"));
copyMenuItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
StringSelection stringSelection = new StringSelection(textToCopy);
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
clipboard.setContents(stringSelection, null);
}
});
JMenuItem selectAllMenuItem = new JMenuItem(AppStrings.translate("text.selectAll"), View.getIcon("selectall16"));
selectAllMenuItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
textSelectionText = text;
textSelectionStartPrecise = 0.0;
textSelectionEndPrecise = (double) fullLength;
textSelectionStartGlyphRect = new Rectangle2D.Double(); //??
textSelectionStartGlyphXPosition = 0.0; //??
repaint();
}
});
if (!textToCopy.isEmpty()) {
pm.add(copyMenuItem);
}
pm.add(selectAllMenuItem);
pm.show(iconPanel, e.getX(), e.getY());
}
}
}
if (e.getClickCount() == 2) {
if (Configuration.showGuides.get() && !Configuration.lockGuides.get()) {
Point mousePoint = e.getPoint();
@@ -4217,7 +4301,7 @@ public final class ImagePanel extends JPanel implements MediaDisplay {
if (text == null || (!allowSelectAllTextTypes && (!(text instanceof DefineEditTextTag) || ((DefineEditTextTag) text).noSelect))) {
text = null;
}
if (text != null && !doFreeTransform) {
if (text != null && !doFreeTransform && !selectionMode) {
Rectangle2D glyphRect = iconPanel.glyphUnderCursorRect;
if (iconPanel.glyphPosUnderCursor > -1 && glyphRect != null) {
if (mouseButton == 1) {
@@ -4721,6 +4805,11 @@ public final class ImagePanel extends JPanel implements MediaDisplay {
hilightedPoints = null;
selectedDepths = new ArrayList<>();
selectedPoints = new ArrayList<>();
textSelectionEndPrecise = null;
textSelectionStartPrecise = null;
textSelectionStartGlyphRect = null;
textSelectionStartGlyphXPosition = null;
textSelectionText = null;
pointEditPanel.setVisible(false);
this.showObjectsUnderCursor = showObjectsUnderCursor;
this.registrationPointPosition = RegistrationPointPosition.CENTER;
@@ -5455,6 +5544,29 @@ public final class ImagePanel extends JPanel implements MediaDisplay {
}
return selStartInt;
}
private int getSelectionEndInt() {
Double selStart = textSelectionStartPrecise;
Double selEnd = textSelectionEndPrecise;
if (selStart == null) {
return -1;
}
if (selStart != null && selEnd != null) {
if (selStart > selEnd) {
double tmp = selStart;
selStart = selEnd;
selEnd = tmp;
}
}
int selEndInt = (int) Math.floor(selEnd);
double endFract = selEnd - selEndInt;
if (endFract > 0.7) {
selEndInt++;
}
return selEndInt;
}
private void drawFrame(Timer thisTimer, boolean display) {
Timelined timelined;
@@ -5779,7 +5891,7 @@ public final class ImagePanel extends JPanel implements MediaDisplay {
}
} else if (handCursor) {
newCursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR);
} else if (selectingText || (iconPanel.mouseOverText != null && (allowSelectAllTextTypes || (iconPanel.mouseOverText instanceof DefineEditTextTag && !((DefineEditTextTag) iconPanel.mouseOverText).noSelect)))) {
} else if (!selectionMode && !doFreeTransform && (selectingText || (iconPanel.mouseOverText != null && (allowSelectAllTextTypes || (iconPanel.mouseOverText instanceof DefineEditTextTag && !((DefineEditTextTag) iconPanel.mouseOverText).noSelect))))) {
newCursor = textCursor;
} else if (zoomAvailable && iconPanel.hasAllowMove()) {
newCursor = Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR);

Binary file not shown.

After

Width:  |  Height:  |  Size: 441 B

View File

@@ -1088,3 +1088,6 @@ abc.link.dialog.noOtherFilesOpened = No other files opened
#after 24.0.1
deobfuscate_options.skip_uninitialized_class_fields_detection = Skip detection of uninitialized class fields
work.decompiling.allScripts.ucf.canBeSkipped = You can turn off this step in the deobfuscation options (icon with the red cross)
text.copy = Copy
text.selectAll = Select all

View File

@@ -1085,4 +1085,7 @@ transform.clipboard.paste.apply = Pou\u017e\u00edt transformaci ze schr\u00e1nky
abc.link.dialog.noOtherFilesOpened = Nejsou otev\u0159eny \u017e\u00e1dn\u00e9 jin\u00e9 soubory
#after 24.0.1
deobfuscate_options.skip_uninitialized_class_fields_detection = P\u0159esko\u010dit detekci neinicializovan\u00fdch pol\u00ed t\u0159\u00edd
deobfuscate_options.skip_uninitialized_class_fields_detection = P\u0159esko\u010dit detekci neinicializovan\u00fdch pol\u00ed t\u0159\u00edd
text.copy = Kop\u00edrovat
text.selectAll = Vybrat v\u0161e