From 96c63be654454c2cc5342942986f69c30758202b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Sat, 19 Nov 2022 13:35:37 +0100 Subject: [PATCH] Added #1680 Pinning objects Fixed Header of display panel not visible on certain color schemes Changed Raw editor does not show tag name in the tree (it's now in the new pinnable head) --- CHANGELOG.md | 4 + README.md | 3 +- .../flash/configuration/Configuration.java | 6 + .../flash/gui/GenericTagTreePanel.java | 6 +- .../decompiler/flash/gui/HeaderLabel.java | 8 +- src/com/jpexs/decompiler/flash/gui/Main.java | 2 +- .../decompiler/flash/gui/MainFrameMenu.java | 4 +- .../jpexs/decompiler/flash/gui/MainPanel.java | 58 ++- .../jpexs/decompiler/flash/gui/PinButton.java | 324 ++++++++++++++++ .../jpexs/decompiler/flash/gui/PinsPanel.java | 361 ++++++++++++++++++ .../flash/gui/graphics/canpin16.png | Bin 0 -> 6179 bytes .../decompiler/flash/gui/graphics/pin16.png | Bin 0 -> 628 bytes .../flash/gui/graphics/pinned16.png | Bin 0 -> 6589 bytes .../locales/AdvancedSettingsDialog.properties | 6 + .../AdvancedSettingsDialog_cs.properties | 6 + .../flash/gui/locales/MainFrame.properties | 6 + .../flash/gui/locales/MainFrame_cs.properties | 8 +- .../gui/taglistview/TagListTreeRoot.java | 3 +- .../flash/gui/tagtree/AbstractTagTree.java | 66 +++- .../decompiler/flash/gui/tagtree/TagTree.java | 16 +- .../flash/gui/tagtree/TagTreeRoot.java | 2 +- .../flash/gui/tagtree/TreeRoot.java | 25 ++ 22 files changed, 866 insertions(+), 48 deletions(-) create mode 100644 src/com/jpexs/decompiler/flash/gui/PinButton.java create mode 100644 src/com/jpexs/decompiler/flash/gui/PinsPanel.java create mode 100644 src/com/jpexs/decompiler/flash/gui/graphics/canpin16.png create mode 100644 src/com/jpexs/decompiler/flash/gui/graphics/pin16.png create mode 100644 src/com/jpexs/decompiler/flash/gui/graphics/pinned16.png create mode 100644 src/com/jpexs/decompiler/flash/gui/tagtree/TreeRoot.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 09a71b9c8..b2cb772e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ All notable changes to this project will be documented in this file. - [#1743] GFX - Adding DefineExternalImage2 and DefineSubImage tags - [#1822], [#1803] AS3 direct editation - optional using AIR (airglobal.swc) to compile - [#1501] Bulk import shapes +- [#1680] Pinning objects ### Fixed - [#1869] Replace references now replaces all references, not just PlaceObject @@ -34,9 +35,11 @@ All notable changes to this project will be documented in this file. - DefineShape4 SVG import NullPointerException - List of objects under cursor and coordinates not showing - ConcurrentModificationException in getCharacters on exit +- Header of display panel not visible on certain color schemes ### Changed - GFX - DefineExternalImage2 no longer handled as character +- Raw editor does not show tag name in the tree (it's now in the new pinnable head) ## [16.3.1] - 2022-11-14 ### Fixed @@ -2616,6 +2619,7 @@ All notable changes to this project will be documented in this file. [#1822]: https://www.free-decompiler.com/flash/issues/1822 [#1803]: https://www.free-decompiler.com/flash/issues/1803 [#1501]: https://www.free-decompiler.com/flash/issues/1501 +[#1680]: https://www.free-decompiler.com/flash/issues/1680 [#1869]: https://www.free-decompiler.com/flash/issues/1869 [#1872]: https://www.free-decompiler.com/flash/issues/1872 [#1692]: https://www.free-decompiler.com/flash/issues/1692 diff --git a/README.md b/README.md index e2ce89746..e185d2600 100644 --- a/README.md +++ b/README.md @@ -157,7 +157,7 @@ And links also these libraries: * [flashdebugger library] (Debugging ActionScript) - LGPLv3 * FFDec Library (LGPLv3) - see below -Application uses also some icons of the [Silk icons pack], [Silk companion 1] and [FatCow icons pack]. +Application uses also some icons of the [Silk icons pack], [Silk companion 1], [FatCow icons pack] and [Aha-Soft icons pack]. ### Library FFDec Library is licensed under GNU LGPL v3 (LGPL-3.0-or-later), see [license.txt](libsrc/ffdec_lib/license.txt) for details. @@ -193,6 +193,7 @@ And also links to these libraries: [Silk icons pack]: http://www.famfamfam.com/lab/icons/silk/ [Silk companion 1]: http://damieng.com/creative/icons/silk-companion-1-icons [FatCow icons pack]: http://www.fatcow.com/free-icons +[Aha-Soft icons pack]: http://www.aha-soft.com [sfntly]: https://code.google.com/p/sfntly/ [JLayer]: http://www.javazoom.net/javalayer/javalayer.html [Animated GIF Writer]: http://elliot.kroo.net/software/java/GifSequenceWriter/ diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/Configuration.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/Configuration.java index 7ff607c37..f90a390d8 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/Configuration.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/Configuration.java @@ -802,6 +802,12 @@ public final class Configuration { @ConfigurationCategory("ui") public static ConfigurationItem showImportShapeInfo = null; + @ConfigurationDefaultString("") + public static ConfigurationItem pinnedItemsTagTreePaths = null; + + @ConfigurationDefaultString("") + public static ConfigurationItem pinnedItemsTagListPaths = null; + private enum OSId { WINDOWS, OSX, UNIX } diff --git a/src/com/jpexs/decompiler/flash/gui/GenericTagTreePanel.java b/src/com/jpexs/decompiler/flash/gui/GenericTagTreePanel.java index 203062964..5c8ac00d7 100644 --- a/src/com/jpexs/decompiler/flash/gui/GenericTagTreePanel.java +++ b/src/com/jpexs/decompiler/flash/gui/GenericTagTreePanel.java @@ -434,7 +434,8 @@ public class GenericTagTreePanel extends GenericTagPanel { super(mainPanel); setLayout(new BorderLayout()); tree = new MyTree(); - + tree.setRootVisible(false); + tree.setShowsRootHandles(true); add(new FasterScrollPane(tree), BorderLayout.CENTER); tree.addMouseListener(new MouseAdapter() { @Override @@ -643,9 +644,10 @@ public class GenericTagTreePanel extends GenericTagPanel { if (component instanceof JLabel) { JLabel lab = (JLabel) component; if (value == tree.getModel().getRoot()) { + //It still does not matter since root is hidden if (editedTag != null) { lab.setIcon(AbstractTagTree.getIconForType(AbstractTagTree.getTreeNodeType(editedTag))); - } + } } } return component; diff --git a/src/com/jpexs/decompiler/flash/gui/HeaderLabel.java b/src/com/jpexs/decompiler/flash/gui/HeaderLabel.java index 4d166ce14..c217c09eb 100644 --- a/src/com/jpexs/decompiler/flash/gui/HeaderLabel.java +++ b/src/com/jpexs/decompiler/flash/gui/HeaderLabel.java @@ -17,6 +17,7 @@ package com.jpexs.decompiler.flash.gui; import com.jpexs.decompiler.flash.configuration.Configuration; +import java.awt.Color; import java.awt.Graphics; import java.awt.SystemColor; import java.awt.geom.GeneralPath; @@ -69,11 +70,14 @@ public class HeaderLabel extends JLabel { @Override public void paint(Graphics g) { + Color foregroundColor; if (Configuration.useRibbonInterface.get()) { SubstanceSkin skin = SubstanceLookAndFeel.getCurrentSkin(); g.setColor(skin.getColorScheme(DecorationAreaType.HEADER, ColorSchemeAssociationKind.FILL, ComponentState.ENABLED).getBackgroundFillColor()); + foregroundColor = skin.getColorScheme(DecorationAreaType.HEADER, ColorSchemeAssociationKind.FILL, ComponentState.ENABLED).getForegroundColor(); } else { g.setColor(SystemColor.control); + foregroundColor = SystemColor.controlText; } g.fillRect(0, 0, getWidth(), getHeight()); if (Configuration.useRibbonInterface.get()) { @@ -94,13 +98,11 @@ public class HeaderLabel extends JLabel { SubstanceSkin skin = SubstanceLookAndFeel.getCurrentSkin(); borderPainter.paintBorder(g, this, getWidth(), getHeight() + dy, contour, contourInner, skin.getColorScheme(DecorationAreaType.HEADER, ColorSchemeAssociationKind.BORDER, ComponentState.ENABLED)); - g.setColor(skin.getColorScheme(DecorationAreaType.HEADER, ColorSchemeAssociationKind.FILL, ComponentState.ENABLED).getForegroundColor()); - } else { - g.setColor(SystemColor.controlText); } JLabel lab = new JLabel(getText(), JLabel.CENTER); lab.setSize(getSize()); + lab.setForeground(foregroundColor); lab.paint(g); } } diff --git a/src/com/jpexs/decompiler/flash/gui/Main.java b/src/com/jpexs/decompiler/flash/gui/Main.java index 501c6f48a..3651d2417 100644 --- a/src/com/jpexs/decompiler/flash/gui/Main.java +++ b/src/com/jpexs/decompiler/flash/gui/Main.java @@ -2559,7 +2559,7 @@ public class Main { searchResultsStorage.save(); } catch (IOException ex) { //ignore - } + } Configuration.saveConfig(); if (mainFrame != null && mainFrame.getPanel() != null) { mainFrame.getPanel().unloadFlashPlayer(); diff --git a/src/com/jpexs/decompiler/flash/gui/MainFrameMenu.java b/src/com/jpexs/decompiler/flash/gui/MainFrameMenu.java index 2f6042ff4..f892e7d3f 100644 --- a/src/com/jpexs/decompiler/flash/gui/MainFrameMenu.java +++ b/src/com/jpexs/decompiler/flash/gui/MainFrameMenu.java @@ -255,8 +255,8 @@ public abstract class MainFrameMenu implements MenuBuilder { } if (swf != null) { - boolean result = Main.closeAll(); - if (result) { + boolean result = Main.closeAll(); + if (result) { swf = null; Timer timer = new Timer(); timer.schedule(new TimerTask() { diff --git a/src/com/jpexs/decompiler/flash/gui/MainPanel.java b/src/com/jpexs/decompiler/flash/gui/MainPanel.java index 1e64df7fb..074cad4da 100644 --- a/src/com/jpexs/decompiler/flash/gui/MainPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/MainPanel.java @@ -100,6 +100,7 @@ import com.jpexs.decompiler.flash.gui.tagtree.AbstractTagTreeModel; import com.jpexs.decompiler.flash.gui.tagtree.TagTree; import com.jpexs.decompiler.flash.gui.tagtree.TagTreeContextMenu; import com.jpexs.decompiler.flash.gui.tagtree.TagTreeModel; +import com.jpexs.decompiler.flash.gui.tagtree.TreeRoot; import com.jpexs.decompiler.flash.gui.timeline.TimelineViewPanel; import com.jpexs.decompiler.flash.helpers.FileTextWriter; import com.jpexs.decompiler.flash.helpers.Freed; @@ -250,6 +251,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.sound.sampled.LineUnavailableException; import javax.sound.sampled.UnsupportedAudioFileException; +import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.Icon; @@ -265,6 +267,10 @@ import javax.swing.JTextField; import javax.swing.JTree; import javax.swing.SwingConstants; import javax.swing.UIManager; +import javax.swing.border.BevelBorder; +import javax.swing.border.Border; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.event.TreeSelectionEvent; @@ -398,10 +404,20 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se private CalculateMissingNeededThread calculateMissingNeededThread; private List> orderedClipboard = new ArrayList<>(); - private Map clipboard = new WeakHashMap<>(); + private Map clipboard = new WeakHashMap<>(); private boolean clipboardCut = false; + + private PinsPanel pinsPanel; + public void savePins() { + pinsPanel.save(); + } + + public void clearPins() { + pinsPanel.clear(); + } + private void handleTreeKeyReleased(KeyEvent e) { AbstractTagTree tree = (AbstractTagTree) e.getSource(); if ((e.getKeyCode() == KeyEvent.VK_UP @@ -923,7 +939,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se statusPanel = new MainFrameStatusPanel(this); add(statusPanel, BorderLayout.SOUTH); - + displayPanel = new JPanel(new CardLayout()); DefaultSyntaxKit.initKit(); @@ -986,9 +1002,20 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se } }); + JPanel rightPanel = new JPanel(new BorderLayout()); + rightPanel.add(displayPanel, BorderLayout.CENTER); + pinsPanel = new PinsPanel(this); + pinsPanel.addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + setTagTreeSelectedNode(getCurrentTree(), pinsPanel.getCurrent()); + } + }); + rightPanel.add(pinsPanel, BorderLayout.NORTH); + //displayPanel.setBorder(BorderFactory.createLineBorder(Color.black)); splitPane2 = new JPersistentSplitPane(JSplitPane.VERTICAL_SPLIT, treePanel, detailPanel, Configuration.guiSplitPane2DividerLocationPercent); - splitPane1 = new JPersistentSplitPane(JSplitPane.HORIZONTAL_SPLIT, splitPane2, displayPanel, Configuration.guiSplitPane1DividerLocationPercent); + splitPane1 = new JPersistentSplitPane(JSplitPane.HORIZONTAL_SPLIT, splitPane2, rightPanel, Configuration.guiSplitPane1DividerLocationPercent); welcomePanel = createWelcomePanel(); add(welcomePanel, BorderLayout.CENTER); @@ -1147,6 +1174,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se enableDrop(true); calculateMissingNeededThread = new CalculateMissingNeededThread(); calculateMissingNeededThread.start(); + pinsPanel.load(); } public void closeTagTreeSearch() { @@ -1204,6 +1232,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se doFilter(); reload(false); View.expandTreeNodes(getCurrentTree(), expandedNodes); + pinsPanel.load(); } public ABCPanel getABCPanel() { @@ -1315,6 +1344,8 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se return false; } } + + clearPins(); List swfsLists = new ArrayList<>(swfs); @@ -1337,7 +1368,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se } for (SWF swf : swfsToClose) { - swf.clearTagSwfs(); + swf.clearTagSwfs(); } refreshTree(); @@ -1385,6 +1416,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se } for (SWF swf : swfsToClose) { Main.searchResultsStorage.destroySwf(swf); + pinsPanel.removeSwf(swf); } swfs.remove(swfList); @@ -4195,6 +4227,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se setTreeModel(view); switch (view) { case VIEW_DUMP: + pinsPanel.setVisible(false); currentView = view; Configuration.lastView.set(currentView); if (!isWelcomeScreen) { @@ -4206,6 +4239,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se reload(true); return true; case VIEW_RESOURCES: + pinsPanel.setVisible(true); currentView = view; Configuration.lastView.set(currentView); if (!isWelcomeScreen) { @@ -4225,6 +4259,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se reload(true); return true; case VIEW_TIMELINE: + pinsPanel.setVisible(false); currentView = view; Configuration.lastView.set(currentView); final SWF swf = getCurrentSwf(); @@ -4247,6 +4282,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se } return false; case VIEW_TAGLIST: + pinsPanel.setVisible(true); currentView = view; Configuration.lastView.set(currentView); if (!isWelcomeScreen) { @@ -4574,11 +4610,8 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se } else { showDetail(DETAILCARDEMPTYPANEL); } - showCard(CARDACTIONSCRIPT3PANEL); - return; - } - - if (treeItem instanceof Tag) { + showCard(CARDACTIONSCRIPT3PANEL); + } else if (treeItem instanceof Tag) { Tag tag = (Tag) treeItem; TagInfo tagInfo = new TagInfo(treeItem.getSwf()); tag.getTagInfo(tagInfo); @@ -4689,9 +4722,14 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se } else if (treeItem instanceof BUTTONRECORD) { showPreview(treeItem, previewPanel, -1, null); showCard(CARDPREVIEWPANEL); - } else { + } else if (!(treeItem instanceof ScriptPack)){ showCard(CARDEMPTYPANEL); } + if (treeItem instanceof TreeRoot) { + pinsPanel.setCurrent(null); + } else { + pinsPanel.setCurrent(treeItem); + } } public void repaintTree() { diff --git a/src/com/jpexs/decompiler/flash/gui/PinButton.java b/src/com/jpexs/decompiler/flash/gui/PinButton.java new file mode 100644 index 000000000..25e90681c --- /dev/null +++ b/src/com/jpexs/decompiler/flash/gui/PinButton.java @@ -0,0 +1,324 @@ +/* + * Copyright (C) 2022 JPEXS + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.jpexs.decompiler.flash.gui; + +import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.gui.tagtree.AbstractTagTree; +import com.jpexs.decompiler.flash.treeitems.TreeItem; +import com.sun.tools.javac.code.Types; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Cursor; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.SystemColor; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.ArrayList; +import java.util.List; +import javax.swing.BorderFactory; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.border.BevelBorder; +import javax.swing.border.Border; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.swing.plaf.basic.BasicPanelUI; +import org.pushingpixels.substance.api.ColorSchemeAssociationKind; +import org.pushingpixels.substance.api.ComponentState; +import org.pushingpixels.substance.api.DecorationAreaType; +import org.pushingpixels.substance.api.SubstanceLookAndFeel; +import org.pushingpixels.substance.api.SubstanceSkin; + +/** + * + * @author JPEXS + */ +public class PinButton extends JPanel { + + //private static final Border raisedBorder = BorderFactory.createCompoundBorder(BorderFactory.createBevelBorder(BevelBorder.RAISED), BorderFactory.createEmptyBorder(3, 5, 0, 5)); + //private static final Border loweredBorder = BorderFactory.createCompoundBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED), BorderFactory.createEmptyBorder(3, 5, 0, 5)); + private List actionListeners = new ArrayList<>(); + private List changeListeners = new ArrayList<>(); + + private boolean pinned; + + private boolean mouseOverPin = false; + private boolean mouseOver = false; + + private JLabel button; + + private TreeItem item; + + private boolean selected; + + private Color color; + private Color hilightedColor; + private Color hilightedTextColor; + private Color selectedColor; + private Color selectedTextColor; + private Color borderColor; + private Color textColor; + + private JLabel label; + + public PinButton(TreeItem item, boolean pinned) { + //setBorder(raisedBorder); + setBorder(BorderFactory.createEmptyBorder(5, 10, 0, 10)); + this.item = item; + this.pinned = pinned; + + if (Configuration.useRibbonInterface.get()) { + SubstanceSkin skin = SubstanceLookAndFeel.getCurrentSkin(); + color = skin.getColorScheme(DecorationAreaType.GENERAL, ColorSchemeAssociationKind.FILL, ComponentState.ENABLED).getBackgroundFillColor(); + hilightedColor = skin.getColorScheme(DecorationAreaType.GENERAL, ColorSchemeAssociationKind.FILL, ComponentState.ROLLOVER_SELECTED).getBackgroundFillColor(); + borderColor = skin.getColorScheme(DecorationAreaType.GENERAL, ColorSchemeAssociationKind.BORDER, ComponentState.ROLLOVER_SELECTED).getUltraDarkColor(); + textColor = skin.getColorScheme(DecorationAreaType.GENERAL, ColorSchemeAssociationKind.FILL, ComponentState.ENABLED).getForegroundColor(); + hilightedTextColor = skin.getColorScheme(DecorationAreaType.GENERAL, ColorSchemeAssociationKind.FILL, ComponentState.ROLLOVER_SELECTED).getForegroundColor(); + } else { + color = SystemColor.control; + hilightedColor = SystemColor.textHighlight; + borderColor = SystemColor.controlShadow; + textColor = SystemColor.controlText; + hilightedTextColor = SystemColor.textHighlightText; + } + + Color color2 = Color.white; + selectedColor = new Color( + (color.getRed() + color2.getRed()) / 2, + (color.getGreen() + color2.getGreen()) / 2, + (color.getBlue() + color2.getBlue()) / 2 + ); + + Color color3 = Color.black; + selectedTextColor = new Color( + (textColor.getRed() + color3.getRed()) / 2, + (textColor.getGreen() + color3.getGreen()) / 2, + (textColor.getBlue() + color3.getBlue()) / 2 + ); + + + label = new JLabel(); + label.setIcon(AbstractTagTree.getIconFor(item)); + label.setText(item.toString()); + + button = new JLabel(); + button.setMinimumSize(new Dimension(10 + 16, 16)); + button.setPreferredSize(new Dimension(10 + 16, 16)); + + MouseAdapter adapter = new MouseAdapter() { + + @Override + public void mousePressed(MouseEvent e) { + if (e.getButton() == MouseEvent.BUTTON1) { + //setBorder(loweredBorder); + } + } + + @Override + public void mouseReleased(MouseEvent e) { + if (e.getButton() == MouseEvent.BUTTON1) { + /*if (selected) { + setBorder(loweredBorder); + } else { + setBorder(raisedBorder); + }*/ + fireAction(); + } + } + + @Override + public void mouseExited(MouseEvent e) { + mouseOver = false; + updateIcon(); + if (selected) { + setBackground(selectedColor); + label.setForeground(selectedTextColor); + } else { + setBackground(color); + label.setForeground(textColor); + } + } + + @Override + public void mouseEntered(MouseEvent e) { + mouseOver = true; + if (selected) { + setBackground(selectedColor); + label.setForeground(selectedTextColor); + } else { + setBackground(hilightedColor); + label.setForeground(hilightedTextColor); + } + } + + @Override + public void mouseMoved(MouseEvent e) { + mouseOver = true; + updateIcon(); + } + + }; + addMouseListener(adapter); + addMouseMotionListener(adapter); + + button.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 0)); + button.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + if (pinned) { + button.setIcon(View.getIcon("pinned16")); + button.setToolTipText(AppStrings.translate("unpin")); + } else { + button.setToolTipText(AppStrings.translate("pin")); + } + MouseAdapter buttonAdapter = new MouseAdapter() { + @Override + public void mouseEntered(MouseEvent e) { + mouseOverPin = true; + mouseOver = true; + if (selected) { + setBackground(selectedColor); + label.setForeground(selectedTextColor); + } else { + setBackground(hilightedColor); + label.setForeground(hilightedTextColor); + } + updateIcon(); + } + + @Override + public void mouseExited(MouseEvent e) { + mouseOverPin = false; + mouseOver = false; + if (selected) { + setBackground(selectedColor); + label.setForeground(selectedTextColor); + } else { + setBackground(color); + label.setForeground(textColor); + } + updateIcon(); + } + + @Override + public void mouseReleased(MouseEvent e) { + if (e.getButton() == MouseEvent.BUTTON1) { + PinButton.this.pinned = !PinButton.this.pinned; + if (PinButton.this.pinned) { + button.setToolTipText(AppStrings.translate("unpin")); + } else { + button.setToolTipText(AppStrings.translate("pin")); + } + updateIcon(); + fireChange(); + } + } + + }; + button.addMouseListener(buttonAdapter); + button.addMouseMotionListener(buttonAdapter); + + setLayout(new BorderLayout()); + add(label, BorderLayout.CENTER); + add(button, BorderLayout.EAST); + } + + private void updateIcon() { + + if (pinned) { + button.setIcon(View.getIcon("pinned16")); + } else if (mouseOverPin) { + button.setIcon(View.getIcon(pinned ? "pinned16" : "pin16")); + } else if (mouseOver) { + button.setIcon(View.getIcon(pinned ? "pinned16" : "canpin16")); + } else { + button.setIcon(null); + } + } + + private void fireAction() { + ActionEvent ev = new ActionEvent(this, 0, ""); + for (ActionListener listener : actionListeners) { + listener.actionPerformed(ev); + } + } + + private void fireChange() { + ChangeEvent ev = new ChangeEvent(this); + for (ChangeListener listener : changeListeners) { + listener.stateChanged(ev); + } + } + + public void addActionListener(ActionListener listener) { + actionListeners.add(listener); + } + + public void removeActionListner(ActionListener listener) { + actionListeners.remove(listener); + } + + public void addChangeListener(ChangeListener listener) { + changeListeners.add(listener); + } + + public void removeChangeListener(ChangeListener listener) { + changeListeners.add(listener); + } + + public boolean isPinned() { + return pinned; + } + + public TreeItem getItem() { + return item; + } + + public boolean isSelected() { + return selected; + } + + public void setSelected(boolean selected) { + this.selected = selected; + if (selected) { + setBackground(selectedColor); + label.setForeground(selectedTextColor); + } else if (!mouseOver) { + setBackground(color); + label.setForeground(textColor); + } + repaint(); + } + + @Override + protected void paintComponent(Graphics g) { + super.paintComponent(g); + g.setColor(borderColor); + g.drawLine(0, 0, getWidth() - 1, 0); + g.drawLine(0, 0, 0, getHeight() - 1); + g.drawLine(getWidth() - 1, 0, getWidth() - 1, getHeight() - 1); + g.drawLine(0, getHeight() -1, getWidth() - 1, getHeight() - 1); + if (selected) { + g.setColor(hilightedColor); + g.drawLine(0, 0, getWidth() - 1, 0); + g.drawLine(0, 1, getWidth() - 1, 1); + g.drawLine(0, 2, getWidth() - 1, 2); + } + } + +} diff --git a/src/com/jpexs/decompiler/flash/gui/PinsPanel.java b/src/com/jpexs/decompiler/flash/gui/PinsPanel.java new file mode 100644 index 000000000..40b7b21ad --- /dev/null +++ b/src/com/jpexs/decompiler/flash/gui/PinsPanel.java @@ -0,0 +1,361 @@ +/* + * Copyright (C) 2022 JPEXS + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.jpexs.decompiler.flash.gui; + +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.gui.tagtree.TreeRoot; +import com.jpexs.decompiler.flash.treeitems.TreeItem; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Pattern; +import javax.swing.BorderFactory; +import javax.swing.JMenuItem; +import javax.swing.JPanel; +import javax.swing.JPopupMenu; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.swing.tree.TreePath; + +/** + * + * @author JPEXS + */ +public class PinsPanel extends JPanel { + + private List items = new ArrayList<>(); + private TreeItem current; + private MainPanel mainPanel; + private PinButton lastSelectedButton; + private PinButton currentUnpinnedButton; + private List buttons = new ArrayList<>(); + private List changeListeners = new ArrayList<>(); + + private List missingTagTreePaths = new ArrayList<>(); + private List missingTagListPaths = new ArrayList<>(); + + + private static final String PATHS_SEPARATOR = "{#sep#}"; + + public PinsPanel(MainPanel mainPanel) { + this.mainPanel = mainPanel; + setLayout(new WrapLayout(WrapLayout.LEFT, 5, 2)); + setBorder(BorderFactory.createEmptyBorder(5, 0, 0, 0)); + } + + public void clear() { + for (TreeItem item : items) { + String tagTreePath = mainPanel.tagTree.getItemPathString(item); + if (tagTreePath == null) { + tagTreePath = ""; + } + + String tagListPath = mainPanel.tagListTree.getItemPathString(item); + if (tagListPath == null) { + tagListPath = ""; + } + + missingTagTreePaths.add(tagTreePath); + missingTagListPaths.add(tagListPath); + } + items.clear(); + rebuild(); + save(); + } + + public void setCurrent(TreeItem item) { + if (lastSelectedButton != null) { + lastSelectedButton.setSelected(false); + } + for (int i = 0; i < items.size(); i++) { + if (items.get(i) == item) { + this.current = item; + buttons.get(i).setSelected(true); + lastSelectedButton = buttons.get(i); + if (currentUnpinnedButton != null) { + remove(currentUnpinnedButton); + revalidate(); + repaint(); + } + return; + } + } + if (this.current == item) { + return; + } + + this.current = item; + + rebuild(); + } + + private String getTreeItemPath(TreeItem item) { + TreePath path = mainPanel.getCurrentTree().getModel().getTreePath(item); + if (path == null) { + return ""; + } + StringBuilder pathString = new StringBuilder(); + for (int i = 1; i < path.getPathCount(); i++) { + if (pathString.length() > 0) { + pathString.append(" / "); + } + pathString.append(path.getPathComponent(i).toString()); + } + return pathString.toString(); + } + + private void rebuild() { + removeAll(); + buttons.clear(); + currentUnpinnedButton = null; + boolean currentPinned = false; + for (TreeItem item : items) { + PinButton pinButton = new PinButton(item, true); + pinButton.setToolTipText(getTreeItemPath(item)); + pinButton.addMouseListener(new MouseAdapter() { + @Override + public void mousePressed(MouseEvent e) { + if (e.getButton() == MouseEvent.BUTTON3) { + JPopupMenu pinMenu = new JPopupMenu(); + JMenuItem unpinMenuItem = new JMenuItem(AppStrings.translate("contextmenu.unpin")); + unpinMenuItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + items.remove(item); + rebuild(); + fireChange(); + } + }); + pinMenu.add(unpinMenuItem); + + JMenuItem unpinAllMenuItem = new JMenuItem(AppStrings.translate("contextmenu.unpin.all")); + unpinAllMenuItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + items.clear(); + rebuild(); + fireChange(); + } + }); + if (items.size() > 1) { + pinMenu.add(unpinAllMenuItem); + } + + JMenuItem unpinOthersMenuItem = new JMenuItem(AppStrings.translate("contextmenu.unpin.others")); + unpinOthersMenuItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + items.clear(); + items.add(item); + rebuild(); + fireChange(); + } + }); + if (items.size() > 1) { + pinMenu.add(unpinOthersMenuItem); + } + pinMenu.show(pinButton, e.getX(), e.getY()); + } + } + + }); + pinButton.addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + if (!pinButton.isPinned()) { + items.remove(item); + rebuild(); + } + save(); + fireChange(); + } + }); + pinButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + current = pinButton.getItem(); + if (lastSelectedButton != null) { + lastSelectedButton.setSelected(false); + if (!lastSelectedButton.isPinned()) { + PinsPanel.this.remove(lastSelectedButton); + revalidate(); + repaint(); + } + } + lastSelectedButton = pinButton; + pinButton.setSelected(true); + fireChange(); + } + }); + add(pinButton); + buttons.add(pinButton); + if (item == current) { + currentPinned = true; + lastSelectedButton = pinButton; + pinButton.setSelected(true); + } + } + if (!currentPinned && current != null) { + currentUnpinnedButton = new PinButton(current, false); + lastSelectedButton = currentUnpinnedButton; + add(currentUnpinnedButton); + currentUnpinnedButton.setToolTipText(getTreeItemPath(current)); + currentUnpinnedButton.setSelected(true); + currentUnpinnedButton.addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + items.add(current); + rebuild(); + save(); + fireChange(); + } + }); + } + revalidate(); + repaint(); + } + + public void addChangeListener(ChangeListener listener) { + changeListeners.add(listener); + } + + public void removeChangeListener(ChangeListener listener) { + changeListeners.add(listener); + } + + private void fireChange() { + ChangeEvent ev = new ChangeEvent(this); + for (ChangeListener listener : changeListeners) { + listener.stateChanged(ev); + } + } + + public List getPinnedItems() { + return new ArrayList<>(items); + } + + public TreeItem getCurrent() { + return current; + } + + public void setPinnedItems(List items) { + this.items = new ArrayList<>(items); + rebuild(); + } + + public void save() { + StringBuilder tagTreePathsBuilder = new StringBuilder(); + StringBuilder tagListPathsBuilder = new StringBuilder(); + boolean first = true; + + for (int i = 0; i < missingTagTreePaths.size(); i++) { + if (!first) { + tagTreePathsBuilder.append(PATHS_SEPARATOR); + tagListPathsBuilder.append(PATHS_SEPARATOR); + } + tagTreePathsBuilder.append(missingTagTreePaths.get(i)); + tagListPathsBuilder.append(missingTagListPaths.get(i)); + first = false; + } + + for (TreeItem item : items) { + String tagTreePath = mainPanel.tagTree.getItemPathString(item); + if (tagTreePath == null) { + tagTreePath = ""; + } + + String tagListPath = mainPanel.tagListTree.getItemPathString(item); + if (tagListPath == null) { + tagListPath = ""; + } + if (!first) { + tagTreePathsBuilder.append(PATHS_SEPARATOR); + tagListPathsBuilder.append(PATHS_SEPARATOR); + } + tagTreePathsBuilder.append(tagTreePath); + tagListPathsBuilder.append(tagListPath); + first = false; + } + + Configuration.pinnedItemsTagTreePaths.set(tagTreePathsBuilder.toString()); + Configuration.pinnedItemsTagListPaths.set(tagListPathsBuilder.toString()); + } + + public void load() { + + final String PATHS_END = "{finish}"; + + List missingTagTreePaths = new ArrayList<>(); + List missingTagListPaths = new ArrayList<>(); + String tagTreePathsCombined = Configuration.pinnedItemsTagTreePaths.get() + PATHS_SEPARATOR + PATHS_END; + String tagListPathsCombined = Configuration.pinnedItemsTagListPaths.get() + PATHS_SEPARATOR + PATHS_END; + String[] tagTreePaths = tagTreePathsCombined.split(Pattern.quote(PATHS_SEPARATOR)); + String[] tagListPaths = tagListPathsCombined.split(Pattern.quote(PATHS_SEPARATOR)); + if (tagTreePaths.length != tagListPaths.length) { + return; + } + List items = new ArrayList<>(); + for (int i = 0; i < tagTreePaths.length - 1; i++) { + String tagTreePath = tagTreePaths[i]; + String tagListPath = tagListPaths[i]; + TreeItem item = mainPanel.tagTree.getTreeItemFromPathString(tagTreePath); + if (item == null || (item instanceof TreeRoot)) { + item = mainPanel.tagListTree.getTreeItemFromPathString(tagListPath); + } + if (item != null && !(item instanceof TreeRoot)) { + items.add(item); + } else { + missingTagTreePaths.add(tagTreePath); + missingTagListPaths.add(tagListPath); + } + } + this.items = items; + this.missingTagTreePaths = missingTagTreePaths; + this.missingTagListPaths = missingTagListPaths; + rebuild(); + } + + public void removeSwf(SWF swf) { + for (int i = 0; i < items.size(); i++) { + TreeItem item = items.get(i); + SWF itemSwf = item.getSwf(); + if (itemSwf == swf || itemSwf == null) { + + String tagTreePath = mainPanel.tagTree.getItemPathString(item); + if (tagTreePath == null) { + tagTreePath = ""; + } + + String tagListPath = mainPanel.tagListTree.getItemPathString(item); + if (tagListPath == null) { + tagListPath = ""; + } + + missingTagTreePaths.add(tagTreePath); + missingTagListPaths.add(tagListPath); + + items.remove(i); + i--; + } + } + save(); + } +} diff --git a/src/com/jpexs/decompiler/flash/gui/graphics/canpin16.png b/src/com/jpexs/decompiler/flash/gui/graphics/canpin16.png new file mode 100644 index 0000000000000000000000000000000000000000..cb075f4d2e71b0eb7fcfb62d00d70b5dea987b9c GIT binary patch literal 6179 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 literal 0 HcmV?d00001 diff --git a/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog.properties b/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog.properties index 5689fbe6c..4e049b7e1 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog.properties @@ -611,3 +611,9 @@ config.description.airLibLocation = Location of airglobal.swc AIR library. It ca config.name.showImportShapeInfo = Show information before importing shapes config.description.showImportShapeInfo = Displays some info about how importing shapes works after clicking Import shapes in the menu. + +config.name.pinnedItemsTagTreePaths = Pinned items paths in tag tree +config.description.pinnedItemsTagTreePaths = Paths of nodes of tag tree which are pinned. + +config.name.pinnedItemsTagListPaths = Pinned items paths in tag list view tree +config.description.pinnedItemsTagListPaths = Paths of nodes of tag list view tree which are pinned. diff --git a/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog_cs.properties b/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog_cs.properties index db751089b..37a0b9ad0 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog_cs.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog_cs.properties @@ -598,3 +598,9 @@ config.description.airLibLocation = Um\u00edst\u011bn\u00ed knihovny AIR s n\u00 config.name.showImportShapeInfo = Zobrazit informaci p\u0159ed importem tvar\u016f config.description.showImportShapeInfo = Zobraz\u00ed n\u011bjak\u00e9 informace o tom jak import tvar\u016f funguje po kliku na import tvar\u016f v menu. + +config.name.pinnedItemsTagTreePaths = Cesty p\u0159ipnut\u00fdch polo\u017eek v stromu tag\u016f +config.description.pinnedItemsTagTreePaths = Cesty uzl\u016f v stromu tag\u016f, kter\u00e9 jsou p\u0159ipnuty. + +config.name.pinnedItemsTagListPaths = Cesty p\u0159ipnut\u00fdch polo\u017eek v stromu seznamu tag\u016f +config.description.pinnedItemsTagListPaths = Cesty uzl\u016f v stromu seznamu tag\u016f, kter\u00e9 jsou p\u0159ipnuty. \ No newline at end of file diff --git a/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties b/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties index 82a90e7f6..25a81a02c 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties @@ -969,3 +969,9 @@ message.info.importShapes = During importing shapes, you need to select a FOLDER The best way to get the structure right is to export shapes in current SWF file first. import.shape.result = %count% shapes imported. + +pin = Click to pin this item +unpin = Pinned - click to unpin this item. +contextmenu.unpin = Unpin +contextmenu.unpin.all = Unpin all +contextmenu.unpin.others = Unpin others \ No newline at end of file diff --git a/src/com/jpexs/decompiler/flash/gui/locales/MainFrame_cs.properties b/src/com/jpexs/decompiler/flash/gui/locales/MainFrame_cs.properties index 7805ec12e..018272265 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/MainFrame_cs.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/MainFrame_cs.properties @@ -940,4 +940,10 @@ message.info.importShapes = B\u011bhem importu tvar\u016f mus\u00edte vybrat SLO Slo\u017eka mus\u00ed obsahovat podslo\u017eku "shapes" a n\u00e1zvy soubor\u016f v n\u00ed mus\u00ed souhlasit s existuj\u00edc\u00edmi tvary v pr\u00e1v\u011b vybran\u00e9m SWF.\r\n \ Nejlep\u0161\u00ed zp\u016fsob jak m\u00edt tuto strukturu spr\u00e1vn\u011b je nejprve exportovat tvary v aktu\u00e1ln\u00edm SWF souboru. -import.shape.result = %count% tvar\u016f importov\u00e1no. \ No newline at end of file +import.shape.result = %count% tvar\u016f importov\u00e1no. + +pin = Kliknut\u00edm tuto polo\u017eku p\u0159ipnete +unpin = P\u0159ipnuto - kliknut\u00edm tuto polo\u017eku odepnete. +contextmenu.unpin = Odepnout +contextmenu.unpin.all = Odepnout v\u0161e +contextmenu.unpin.others = Odepnout ostatn\u00ed \ No newline at end of file diff --git a/src/com/jpexs/decompiler/flash/gui/taglistview/TagListTreeRoot.java b/src/com/jpexs/decompiler/flash/gui/taglistview/TagListTreeRoot.java index b5096f348..fa1b82bac 100644 --- a/src/com/jpexs/decompiler/flash/gui/taglistview/TagListTreeRoot.java +++ b/src/com/jpexs/decompiler/flash/gui/taglistview/TagListTreeRoot.java @@ -17,13 +17,14 @@ package com.jpexs.decompiler.flash.gui.taglistview; import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.gui.tagtree.TreeRoot; import com.jpexs.decompiler.flash.treeitems.TreeItem; /** * * @author JPEXS */ -public class TagListTreeRoot implements TreeItem { +public class TagListTreeRoot implements TreeItem, TreeRoot{ @Override public SWF getSwf() { diff --git a/src/com/jpexs/decompiler/flash/gui/tagtree/AbstractTagTree.java b/src/com/jpexs/decompiler/flash/gui/tagtree/AbstractTagTree.java index 5f598c126..b3136ddd4 100644 --- a/src/com/jpexs/decompiler/flash/gui/tagtree/AbstractTagTree.java +++ b/src/com/jpexs/decompiler/flash/gui/tagtree/AbstractTagTree.java @@ -149,6 +149,28 @@ public abstract class AbstractTagTree extends JTree { return ICONS.get(t); } + public static Icon getIconFor(TreeItem val) { + return getIconFor(val, false); + } + public static Icon getIconFor(TreeItem val, boolean folderExpanded) { + TreeNodeType type = getTreeNodeType(val); + + if (type == TreeNodeType.FOLDER && folderExpanded) { + type = TreeNodeType.FOLDER_OPEN; + } + + if ((type == TreeNodeType.FOLDER || type == TreeNodeType.FOLDER_OPEN) && val instanceof FolderItem) { + FolderItem si = (FolderItem) val; + if (!TagTreeRoot.FOLDER_ROOT.equals(si.getName())) { + String itemName = "folder" + si.getName(); + return View.getIcon(itemName.toLowerCase(Locale.ENGLISH) + "16"); + } + } else { + return getIconForType(type); + } + return null; + } + public AbstractTagTree(AbstractTagTreeModel treeModel, MainPanel mainPanel) { super(treeModel); this.mainPanel = mainPanel; @@ -401,15 +423,27 @@ public abstract class AbstractTagTree extends JTree { } } + public TreePath getTreePathFromString(String pathStr) { + if (pathStr == null || pathStr.length() == 0) { + return null; + } + String[] path = pathStr.split("\\|"); + return View.getTreePathByPathStrings(this, Arrays.asList(path)); + } + + public TreeItem getTreeItemFromPathString(String pathStr) { + TreePath path = getTreePathFromString(pathStr); + if (path == null) { + return null; + } + return (TreeItem) path.getLastPathComponent(); + } + public void setSelectionPathString(String pathStr) { - if (pathStr != null && pathStr.length() > 0) { - String[] path = pathStr.split("\\|"); - - TreePath tp = View.getTreePathByPathStrings(this, Arrays.asList(path)); - if (tp != null) { - // the current view is the Resources view, otherwise tp is null - mainPanel.setTagTreeSelectedNode(this, (TreeItem) tp.getLastPathComponent()); - } + TreeItem item = getTreeItemFromPathString(pathStr); + if (item != null) { + // the current view is the Resources view, otherwise tp is null + mainPanel.setTagTreeSelectedNode(this, item); } } @@ -563,9 +597,16 @@ public abstract class AbstractTagTree extends JTree { return item; } - public String getSelectionPathString() { + public String getItemPathString(TreeItem item) { + TreePath path = getModel().getTreePath(item); + if (path == null) { + return null; + } + return pathToString(path); + } + + public String pathToString(TreePath path) { StringBuilder sb = new StringBuilder(); - TreePath path = getSelectionPath(); if (path != null) { boolean first = true; for (Object p : path.getPath()) { @@ -577,10 +618,13 @@ public abstract class AbstractTagTree extends JTree { sb.append(p.toString()); } } - return sb.toString(); } + public String getSelectionPathString() { + return pathToString(getSelectionPath()); + } + public static TreeNodeType getTagNodeTypeFromTagClass(Class cl) { if ((cl == DefineFontTag.class) || (cl == DefineFont2Tag.class) diff --git a/src/com/jpexs/decompiler/flash/gui/tagtree/TagTree.java b/src/com/jpexs/decompiler/flash/gui/tagtree/TagTree.java index 8055f5ea4..79914885e 100644 --- a/src/com/jpexs/decompiler/flash/gui/tagtree/TagTree.java +++ b/src/com/jpexs/decompiler/flash/gui/tagtree/TagTree.java @@ -168,21 +168,7 @@ public class TagTree extends AbstractTagTree { return this; } - TreeNodeType type = getTreeNodeType(val); - - if (type == TreeNodeType.FOLDER && expanded) { - type = TreeNodeType.FOLDER_OPEN; - } - - if ((type == TreeNodeType.FOLDER || type == TreeNodeType.FOLDER_OPEN) && val instanceof FolderItem) { - FolderItem si = (FolderItem) val; - if (!TagTreeRoot.FOLDER_ROOT.equals(si.getName())) { - String itemName = "folder" + si.getName(); - setIcon(View.getIcon(itemName.toLowerCase(Locale.ENGLISH) + "16")); - } - } else { - setIcon(getIconForType(type)); - } + setIcon(getIconFor(val, expanded)); /* boolean isModified = val instanceof Tag && ((Tag) val).isModified(); if(val instanceof ScriptPack){ diff --git a/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeRoot.java b/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeRoot.java index 89ac72b57..9d749b531 100644 --- a/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeRoot.java +++ b/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeRoot.java @@ -22,7 +22,7 @@ import com.jpexs.decompiler.flash.treeitems.FolderItem; * * @author JPEXS */ -public class TagTreeRoot extends FolderItem { +public class TagTreeRoot extends FolderItem implements TreeRoot { public static final String FOLDER_ROOT = "root"; diff --git a/src/com/jpexs/decompiler/flash/gui/tagtree/TreeRoot.java b/src/com/jpexs/decompiler/flash/gui/tagtree/TreeRoot.java new file mode 100644 index 000000000..e99c28a91 --- /dev/null +++ b/src/com/jpexs/decompiler/flash/gui/tagtree/TreeRoot.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2022 JPEXS + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.jpexs.decompiler.flash.gui.tagtree; + +/** + * + * @author JPEXS + */ +public interface TreeRoot { + +}