mirror of
https://git.huckle.dev/Huckles-Minecraft-Archive/jpexs-decompiler.git
synced 2026-06-12 04:24:50 +00:00
Added Drag and drop to move/copy tags in the tag list view
This commit is contained in:
@@ -6,6 +6,7 @@ All notable changes to this project will be documented in this file.
|
||||
- Allowed copy/cut tags to clipboard across multiple SWFs
|
||||
- Keyboard shortcuts for tag clipboard operations
|
||||
- Hilight clipboard panel on copy/cut action for a few seconds
|
||||
- Drag and drop to move/copy tags in the tag list view
|
||||
|
||||
## [16.2.0] - 2022-11-08
|
||||
### Added
|
||||
|
||||
@@ -399,65 +399,65 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se
|
||||
|
||||
private boolean clipboardCut = false;
|
||||
|
||||
|
||||
private void handleTreeKeyPressed(KeyEvent e) {
|
||||
AbstractTagTree tree = (AbstractTagTree)e.getSource();
|
||||
AbstractTagTree tree = (AbstractTagTree) e.getSource();
|
||||
if ((e.getKeyCode() == 'C' || e.getKeyCode() == 'X') && (e.isControlDown())) {
|
||||
TreePath[] paths = tree.getSelectionPaths();
|
||||
if (paths == null || paths.length == 0) {
|
||||
return;
|
||||
}
|
||||
TreePath[] paths = tree.getSelectionPaths();
|
||||
if (paths == null || paths.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<TreeItem> tagItems = new ArrayList<>();
|
||||
for (TreePath treePath : paths) {
|
||||
TreeItem item = (TreeItem) treePath.getLastPathComponent();
|
||||
if (item instanceof TagScript) {
|
||||
tagItems.add(((TagScript) item).getTag());
|
||||
} else if (item instanceof Tag) {
|
||||
tagItems.add((Tag) item);
|
||||
List<TreeItem> tagItems = new ArrayList<>();
|
||||
for (TreePath treePath : paths) {
|
||||
TreeItem item = (TreeItem) treePath.getLastPathComponent();
|
||||
if (item instanceof TagScript) {
|
||||
tagItems.add(((TagScript) item).getTag());
|
||||
} else if (item instanceof Tag) {
|
||||
tagItems.add((Tag) item);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (e.getKeyCode() == 'C') {
|
||||
if (e.isShiftDown()) {
|
||||
contextPopupMenu.copyTagToClipboardWithDependenciesActionPerformed(null, tagItems);
|
||||
} else {
|
||||
contextPopupMenu.copyTagToClipboardActionPerformed(null, tagItems);
|
||||
if (e.getKeyCode() == 'C') {
|
||||
if (e.isShiftDown()) {
|
||||
contextPopupMenu.copyTagToClipboardWithDependenciesActionPerformed(null, tagItems);
|
||||
} else {
|
||||
contextPopupMenu.copyTagToClipboardActionPerformed(null, tagItems);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (e.getKeyCode() == 'X') {
|
||||
if (e.isShiftDown()) {
|
||||
contextPopupMenu.cutTagToClipboardWithDependenciesActionPerformed(null);
|
||||
} else {
|
||||
contextPopupMenu.cutTagToClipboardActionPerformed(null);
|
||||
if (e.getKeyCode() == 'X') {
|
||||
if (e.isShiftDown()) {
|
||||
contextPopupMenu.cutTagToClipboardWithDependenciesActionPerformed(null);
|
||||
} else {
|
||||
contextPopupMenu.cutTagToClipboardActionPerformed(null);
|
||||
}
|
||||
}
|
||||
repaintTree();
|
||||
}
|
||||
repaintTree();
|
||||
}
|
||||
if (e.getKeyCode() == 'V' && e.isControlDown()) {
|
||||
TreePath[] paths = tree.getSelectionPaths();
|
||||
if (paths == null || paths.length == 0) {
|
||||
return;
|
||||
}
|
||||
if (e.getKeyCode() == 'V' && e.isControlDown()) {
|
||||
TreePath[] paths = tree.getSelectionPaths();
|
||||
if (paths == null || paths.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<TreeItem> items = new ArrayList<>();
|
||||
for (TreePath treePath : paths) {
|
||||
TreeItem item = (TreeItem) treePath.getLastPathComponent();
|
||||
items.add(item);
|
||||
List<TreeItem> items = new ArrayList<>();
|
||||
for (TreePath treePath : paths) {
|
||||
TreeItem item = (TreeItem) treePath.getLastPathComponent();
|
||||
items.add(item);
|
||||
}
|
||||
if (items.size() > 1) {
|
||||
return;
|
||||
}
|
||||
TreeItem firstItem = items.get(0);
|
||||
if (!((firstItem instanceof Tag) || (firstItem instanceof Frame))) {
|
||||
return;
|
||||
}
|
||||
if (e.isShiftDown()) {
|
||||
contextPopupMenu.pasteAfterActionPerformed(null);
|
||||
} else {
|
||||
contextPopupMenu.pasteBeforeActionPerformed(null);
|
||||
}
|
||||
}
|
||||
if (items.size() > 1) {
|
||||
return;
|
||||
}
|
||||
TreeItem firstItem = items.get(0);
|
||||
if (!((firstItem instanceof Tag) || (firstItem instanceof Frame))) {
|
||||
return;
|
||||
}
|
||||
if (e.isShiftDown()) {
|
||||
contextPopupMenu.pasteAfterActionPerformed(null);
|
||||
} else {
|
||||
contextPopupMenu.pasteBeforeActionPerformed(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void gcClipboard() {
|
||||
for (int i = orderedClipboard.size() - 1; i >= 0; i--) {
|
||||
WeakReference<TreeItem> ref = orderedClipboard.get(i);
|
||||
@@ -4830,9 +4830,11 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se
|
||||
calculateMissingNeededCharacters(missingNeededCharacters, swf);
|
||||
}
|
||||
}
|
||||
this.missingNeededCharacters = missingNeededCharacters;
|
||||
tagTree.setMissingNeededCharacters(missingNeededCharacters);
|
||||
tagListTree.setMissingNeededCharacters(missingNeededCharacters);
|
||||
if (!missingNeededCharacters.equals(this.missingNeededCharacters)) {
|
||||
this.missingNeededCharacters = missingNeededCharacters;
|
||||
tagTree.setMissingNeededCharacters(missingNeededCharacters);
|
||||
tagListTree.setMissingNeededCharacters(missingNeededCharacters);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -21,29 +21,49 @@ import com.jpexs.decompiler.flash.gui.MainPanel;
|
||||
import com.jpexs.decompiler.flash.gui.tagtree.AbstractTagTree;
|
||||
import static com.jpexs.decompiler.flash.gui.tagtree.AbstractTagTree.getSelection;
|
||||
import com.jpexs.decompiler.flash.tags.DoInitActionTag;
|
||||
import com.jpexs.decompiler.flash.tags.Tag;
|
||||
import com.jpexs.decompiler.flash.timeline.Frame;
|
||||
import com.jpexs.decompiler.flash.timeline.Timelined;
|
||||
import com.jpexs.decompiler.flash.treeitems.TreeItem;
|
||||
import java.awt.datatransfer.DataFlavor;
|
||||
import java.awt.datatransfer.Transferable;
|
||||
import java.awt.datatransfer.UnsupportedFlavorException;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import javax.swing.DropMode;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JTree;
|
||||
import javax.swing.TransferHandler;
|
||||
import javax.swing.tree.TreePath;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class TagListTree extends AbstractTagTree {
|
||||
|
||||
|
||||
public TagListTree(TagListTreeModel model, MainPanel mainPanel) {
|
||||
super(model, mainPanel);
|
||||
setCellRenderer(new TagListTreeCellRenderer());
|
||||
}
|
||||
|
||||
super(model, mainPanel);
|
||||
setCellRenderer(new TagListTreeCellRenderer());
|
||||
setDragEnabled(true);
|
||||
setDropMode(DropMode.ON_OR_INSERT);
|
||||
setTransferHandler(new TreeTransferHandler(mainPanel));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TreeItem> getSelection(SWF swf) {
|
||||
return getSelection(swf, getAllSelected());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public TagListTreeModel getModel() {
|
||||
return (TagListTreeModel) super.getModel();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String convertValueToText(Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
|
||||
@@ -51,7 +71,7 @@ public class TagListTree extends AbstractTagTree {
|
||||
DoInitActionTag tag = (DoInitActionTag) value;
|
||||
return DoInitActionTag.NAME + " (" + tag.spriteId + ")";
|
||||
}
|
||||
if(value != null) {
|
||||
if (value != null) {
|
||||
String sValue = value.toString();
|
||||
if (sValue != null) {
|
||||
return sValue;
|
||||
@@ -60,5 +80,173 @@ public class TagListTree extends AbstractTagTree {
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
class TreeTransferHandler extends TransferHandler {
|
||||
|
||||
DataFlavor nodesFlavor;
|
||||
DataFlavor[] flavors = new DataFlavor[1];
|
||||
JTree.DropLocation dropLocation = null;
|
||||
MainPanel mainPanel;
|
||||
|
||||
public TreeTransferHandler(MainPanel mainPanel) {
|
||||
this.mainPanel = mainPanel;
|
||||
try {
|
||||
String mimeType = DataFlavor.javaJVMLocalObjectMimeType
|
||||
+ ";class=\""
|
||||
+ Tag[].class.getName()
|
||||
+ "\"";
|
||||
nodesFlavor = new DataFlavor(mimeType);
|
||||
flavors[0] = nodesFlavor;
|
||||
} catch (ClassNotFoundException e) {
|
||||
System.err.println("ClassNotFound: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canImport(TransferSupport support) {
|
||||
if (!support.isDrop()) {
|
||||
return false;
|
||||
}
|
||||
support.setShowDropLocation(true);
|
||||
if (!support.isDataFlavorSupported(nodesFlavor)) {
|
||||
return false;
|
||||
}
|
||||
// Do not allow a drop on the drag source selections.
|
||||
JTree.DropLocation dl = (JTree.DropLocation) support.getDropLocation();
|
||||
|
||||
if ((dl.getPath().getLastPathComponent() instanceof Tag) && dl.getChildIndex() == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
AbstractTagTree tree = (AbstractTagTree) support.getComponent();
|
||||
|
||||
List<TreeItem> selected = tree.getSelected();
|
||||
TreePath destPath = dl.getPath();
|
||||
List<TreeItem> parents = new ArrayList<>();
|
||||
for(int i = 0; i < destPath.getPathCount(); i++) {
|
||||
parents.add((TreeItem) destPath.getPathComponent(i));
|
||||
}
|
||||
for (TreeItem item : selected) {
|
||||
if (parents.contains(item)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int dropRow = tree.getRowForPath(dl.getPath());
|
||||
int[] selRows = tree.getSelectionRows();
|
||||
|
||||
for (int i = 0; i < selRows.length; i++) {
|
||||
if (selRows[i] == dropRow) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Transferable createTransferable(JComponent c) {
|
||||
AbstractTagTree tree = (AbstractTagTree) c;
|
||||
dropLocation = null;
|
||||
TreePath[] paths = tree.getSelectionPaths();
|
||||
if (paths == null) {
|
||||
return null;
|
||||
}
|
||||
List<Tag> tags = new ArrayList<>();
|
||||
for (TreePath path : paths) {
|
||||
if (path.getLastPathComponent() instanceof Tag) {
|
||||
tags.add((Tag) path.getLastPathComponent());
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Tag tagArr[] = tags.toArray(new Tag[tags.size()]);
|
||||
return new TagsTransferable(tagArr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSourceActions(JComponent c) {
|
||||
return COPY_OR_MOVE;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void exportDone(JComponent source, Transferable data, int action) {
|
||||
AbstractTagTree tree = (AbstractTagTree) source;
|
||||
if (dropLocation == null) {
|
||||
return;
|
||||
}
|
||||
int childIndex = dropLocation.getChildIndex();
|
||||
TreeItem dest = (TreeItem) dropLocation.getPath().getLastPathComponent();
|
||||
Set<TreeItem> sourceItems = new LinkedHashSet<>(tree.getSelected());
|
||||
Timelined timelined;
|
||||
Tag position;
|
||||
if (childIndex == -1) {
|
||||
if (dest instanceof Tag) {
|
||||
timelined = ((Tag) dest).getTimelined();
|
||||
position = (Tag) dest;
|
||||
} else if (dest instanceof Frame) {
|
||||
Frame frame = (Frame) dest;
|
||||
position = frame.allInnerTags.get(frame.allInnerTags.size() - 1);
|
||||
timelined = frame.timeline.timelined;
|
||||
} else {
|
||||
timelined = (Timelined) dest;
|
||||
position = null;
|
||||
}
|
||||
} else {
|
||||
if (dest instanceof Frame) {
|
||||
Frame frame = (Frame) dest;
|
||||
timelined = frame.timeline.timelined;
|
||||
position = childIndex == frame.allInnerTags.size() ? null : frame.allInnerTags.get(childIndex);
|
||||
} else {
|
||||
timelined = ((Tag) dest).getTimelined();
|
||||
position = (Tag) dest;
|
||||
}
|
||||
}
|
||||
mainPanel.getContextPopupMenu().copyOrMoveTagsBeforeAfter(sourceItems, (action & MOVE) == MOVE, timelined, position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean importData(TransferSupport support) {
|
||||
if (!canImport(support)) {
|
||||
return false;
|
||||
}
|
||||
Transferable t = support.getTransferable();
|
||||
Tag[] tags = null;
|
||||
try {
|
||||
tags = (Tag[]) t.getTransferData(nodesFlavor);
|
||||
} catch (UnsupportedFlavorException | IOException ex) {
|
||||
Logger.getLogger(TreeTransferHandler.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
|
||||
dropLocation = (JTree.DropLocation) support.getDropLocation();
|
||||
return true;
|
||||
}
|
||||
|
||||
public class TagsTransferable implements Transferable {
|
||||
|
||||
Tag[] nodes;
|
||||
|
||||
public TagsTransferable(Tag[] nodes) {
|
||||
this.nodes = nodes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getTransferData(DataFlavor flavor)
|
||||
throws UnsupportedFlavorException {
|
||||
if (!isDataFlavorSupported(flavor)) {
|
||||
throw new UnsupportedFlavorException(flavor);
|
||||
}
|
||||
return nodes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataFlavor[] getTransferDataFlavors() {
|
||||
return flavors;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDataFlavorSupported(DataFlavor flavor) {
|
||||
return nodesFlavor.equals(flavor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
package com.jpexs.decompiler.flash.gui.taglistview;
|
||||
|
||||
import com.jpexs.decompiler.flash.gui.AppStrings;
|
||||
import com.jpexs.decompiler.flash.gui.TreeNodeType;
|
||||
import com.jpexs.decompiler.flash.gui.View;
|
||||
import com.jpexs.decompiler.flash.gui.tagtree.AbstractTagTree;
|
||||
import com.jpexs.decompiler.flash.gui.tagtree.TagTree;
|
||||
@@ -57,9 +56,9 @@ public class TagListTreeCellRenderer extends DefaultTreeCellRenderer {
|
||||
setUI(new BasicLabelUI());
|
||||
setOpaque(false);
|
||||
setBackgroundNonSelectionColor(Color.white);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintComponent(Graphics g) {
|
||||
super.paintComponent(g);
|
||||
@@ -73,8 +72,8 @@ public class TagListTreeCellRenderer extends DefaultTreeCellRenderer {
|
||||
g2d.fillRect(0, 0, getWidth(), getHeight());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
|
||||
@@ -2740,7 +2740,11 @@ public class TagTreeContextMenu extends JPopupMenu {
|
||||
}
|
||||
ReadOnlyTagList tags = timelined.getTags();
|
||||
position = positionInt < tags.size() ? tags.get(positionInt) : null;
|
||||
pasteBeforeAfter(true, timelined, position);
|
||||
copyOrMoveTagsBeforeAfter(mainPanel.getClipboardContents(), mainPanel.isClipboardCut(), timelined, position);
|
||||
|
||||
if (mainPanel.isClipboardCut()) {
|
||||
mainPanel.emptyClipboard();
|
||||
}
|
||||
}
|
||||
|
||||
public void pasteAfterActionPerformed(ActionEvent evt) {
|
||||
@@ -2758,7 +2762,11 @@ public class TagTreeContextMenu extends JPopupMenu {
|
||||
}
|
||||
ReadOnlyTagList tags = timelined.getTags();
|
||||
position = positionInt < tags.size() ? tags.get(positionInt) : null;
|
||||
pasteBeforeAfter(false, timelined, position);
|
||||
copyOrMoveTagsBeforeAfter(mainPanel.getClipboardContents(), mainPanel.isClipboardCut(), timelined, position);
|
||||
|
||||
if (mainPanel.isClipboardCut()) {
|
||||
mainPanel.emptyClipboard();
|
||||
}
|
||||
}
|
||||
|
||||
private void pasteInsideActionPerformed(ActionEvent evt) {
|
||||
@@ -2773,11 +2781,32 @@ public class TagTreeContextMenu extends JPopupMenu {
|
||||
timelined = (Timelined) item;
|
||||
position = null;
|
||||
}
|
||||
pasteBeforeAfter(false, timelined, position);
|
||||
copyOrMoveTagsBeforeAfter(mainPanel.getClipboardContents(), mainPanel.isClipboardCut(), timelined, position);
|
||||
|
||||
if (mainPanel.isClipboardCut()) {
|
||||
mainPanel.emptyClipboard();
|
||||
}
|
||||
}
|
||||
|
||||
private void pasteBeforeAfter(boolean before, Timelined targetTimelined, Tag position) {
|
||||
Set<TreeItem> items = mainPanel.getClipboardContents();
|
||||
public void copyOrMoveTagsBeforeAfter(Set<TreeItem> items, boolean move, Timelined targetTimelined, Tag position) {
|
||||
//do not move to self
|
||||
if (items.size() == 1 && move) {
|
||||
Tag tag = (Tag) items.iterator().next();
|
||||
if (targetTimelined == tag.getTimelined()) {
|
||||
if (tag == position) {
|
||||
return;
|
||||
}
|
||||
int index = tag.getTimelined().indexOfTag(tag);
|
||||
ReadOnlyTagList tags = tag.getTimelined().getTags();
|
||||
Tag nextPosition;
|
||||
if (index + 1 < tags.size()) {
|
||||
nextPosition = tags.get(index + 1);
|
||||
if (nextPosition == position) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Set<SWF> sourceSwfs = new LinkedHashSet<>();
|
||||
SWF targetSwf = (targetTimelined instanceof SWF) ? (SWF) targetTimelined : ((DefineSpriteTag) targetTimelined).getSwf();
|
||||
try {
|
||||
@@ -2791,7 +2820,15 @@ public class TagTreeContextMenu extends JPopupMenu {
|
||||
SWF sourceSwf = tag.getSwf();
|
||||
sourceSwfs.add(sourceSwf);
|
||||
|
||||
if (mainPanel.isClipboardCut()) {
|
||||
if (move) {
|
||||
if (tag == position) {
|
||||
int index = tag.getTimelined().indexOfTag(position);
|
||||
if (tag.getTimelined().getTags().size() > index + 1) {
|
||||
position = tag.getTimelined().getTags().get(index + 1);
|
||||
} else {
|
||||
position = null;
|
||||
}
|
||||
}
|
||||
tag.getTimelined().removeTag(tag);
|
||||
}
|
||||
|
||||
@@ -2808,7 +2845,7 @@ public class TagTreeContextMenu extends JPopupMenu {
|
||||
realTargetTimelined = spriteTag.getTimelined(); //should be SWF
|
||||
realPosition = spriteTag;
|
||||
}
|
||||
if (sourceSwf != targetSwf || !mainPanel.isClipboardCut()) {
|
||||
if (sourceSwf != targetSwf || !move) {
|
||||
ReadOnlyTagList tags = realTargetTimelined.getTags();
|
||||
int positionInt = realPosition == null ? tags.size() : tags.indexOf(realPosition);
|
||||
|
||||
@@ -2828,7 +2865,7 @@ public class TagTreeContextMenu extends JPopupMenu {
|
||||
targetSwf.getCharacters(); // force rebuild character id cache
|
||||
copyTag.setModified(true);
|
||||
newTags.add(copyTag);
|
||||
} else if (sourceSwf == targetSwf && mainPanel.isClipboardCut()) {
|
||||
} else if (sourceSwf == targetSwf && move) { //mainPanel.isClipboardCut()
|
||||
ReadOnlyTagList tags = realTargetTimelined.getTags();
|
||||
int positionInt = realPosition == null ? tags.size() : tags.indexOf(realPosition);
|
||||
realTargetTimelined.addTag(positionInt, tag);
|
||||
@@ -2857,10 +2894,7 @@ public class TagTreeContextMenu extends JPopupMenu {
|
||||
targetSwf.clearImageCache();
|
||||
targetSwf.updateCharacters();
|
||||
targetSwf.resetTimelines(targetSwf);
|
||||
|
||||
if (mainPanel.isClipboardCut()) {
|
||||
mainPanel.emptyClipboard();
|
||||
}
|
||||
|
||||
mainPanel.refreshTree(targetSwf);
|
||||
} catch (InterruptedException | IOException ex) {
|
||||
Logger.getLogger(TagTreeContextMenu.class.getName()).log(Level.SEVERE, null, ex);
|
||||
|
||||
Reference in New Issue
Block a user