/*
* Copyright (C) 2022-2025 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.abc.ABC;
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.FlowLayout;
import java.awt.Image;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.DefaultTableModel;
/**
* @author JPEXS
*/
public class BreakpointListDialog extends AppDialog {
private SWF swf;
private JTable table = new JTable();
private List breakpointList = new ArrayList<>();
private Timer refreshTimer = null;
private static final int REFRESH_TIMEOUT = 1000;
private class Breakpoint {
public String scriptName;
public int line;
public Breakpoint(String scriptName, int line) {
this.scriptName = scriptName;
this.line = line;
}
}
public BreakpointListDialog(Window owner, SWF swf) {
super(owner);
setDefaultCloseOperation(HIDE_ON_CLOSE);
setTitle(translate("dialog.title") + " - " + swf.getShortFileName());
this.swf = swf;
table.setCellSelectionEnabled(false);
table.setRowSelectionAllowed(true);
table.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (SwingUtilities.isLeftMouseButton(e) && e.getClickCount() == 2) {
int row = table.rowAtPoint(e.getPoint());
if (table.getSelectedRow() != -1 && row != -1) {
gotoButtonActionPerformed(null);
}
}
}
});
Container cnt = getContentPane();
cnt.setLayout(new BorderLayout());
cnt.add(new JScrollPane(table), BorderLayout.CENTER);
JPanel buttonsPanel = new JPanel(new FlowLayout());
JButton gotoButton = new JButton(translate("button.goto"));
gotoButton.addActionListener(this::gotoButtonActionPerformed);
buttonsPanel.add(gotoButton);
JButton removeButton = new JButton(translate("button.remove"));
removeButton.addActionListener(this::removeButtonActionPerformed);
buttonsPanel.add(removeButton);
JButton removeAllButton = new JButton(translate("button.removeAll"));
removeAllButton.addActionListener(this::removeAllButtonActionPerformed);
buttonsPanel.add(removeAllButton);
JButton closeButton = new JButton(translate("button.close"));
closeButton.addActionListener(this::closeButtonActionPerformed);
buttonsPanel.add(closeButton);
cnt.add(buttonsPanel, BorderLayout.SOUTH);
refresh();
setSize(500, 300);
//View.setWindowIcon(this);
List images = new ArrayList<>();
images.add(View.loadImage("breakpointlist16"));
setIconImages(images);
View.centerScreen(this);
}
private Breakpoint getSelected() {
int row = table.getSelectedRow();
if (row == -1) {
return null;
}
return breakpointList.get(row);
}
private void closeButtonActionPerformed(ActionEvent evt) {
setVisible(false);
}
private void removeButtonActionPerformed(ActionEvent evt) {
Breakpoint breakpoint = getSelected();
if (breakpoint == null) {
return;
}
Main.getDebugHandler().removeBreakPoint(swf, breakpoint.scriptName, breakpoint.line);
refreshMarkers();
refresh();
}
private void refreshMarkers() {
if (swf.isAS3()) {
Main.getMainFrame().getPanel().getABCPanel().decompiledTextArea.refreshMarkers();
Main.getMainFrame().getPanel().getABCPanel().detailPanel.methodTraitPanel.methodCodePanel.refreshMarkers();
} else {
Main.getMainFrame().getPanel().getActionPanel().decompiledEditor.refreshMarkers();
Main.getMainFrame().getPanel().getActionPanel().editor.refreshMarkers();
}
}
private void removeAllButtonActionPerformed(ActionEvent evt) {
Main.getDebugHandler().clearBreakPoints(swf);
refreshMarkers();
refresh();
}
private void gotoButtonActionPerformed(ActionEvent evt) {
Breakpoint breakpoint = getSelected();
if (breakpoint == null) {
return;
}
int classIndex = -1;
int traitIndex = -1;
int methodIndex = -1;
/*if (Main.getDebugHandler().getDebuggedSwf() == swf) {
int f = Main.getDebugHandler().moduleIdOf(breakpoint.scriptName);
if (f != -1) {
Integer newClassIndex = Main.getDebugHandler().moduleToClassIndex(f);
classIndex = newClassIndex == null ? -1 : newClassIndex;
Integer newMethodIndex = Main.getDebugHandler().moduleToMethodIndex(f);
methodIndex = newMethodIndex == null ? -1 : newMethodIndex;
Integer newTraitIndex = Main.getDebugHandler().moduleToTraitIndex(f);;
traitIndex = newTraitIndex == null ? -1 : newTraitIndex;
}
}*/
Pattern abcPcodePattern = Pattern.compile("^#PCODE abc:(?[0-9]+),body:(?[0-9]+);.*");
Matcher m = abcPcodePattern.matcher(breakpoint.scriptName);
if (m.matches()) {
int abcIndex = Integer.parseInt(m.group("abc"));
int bodyIndex = Integer.parseInt(m.group("body"));
ABC abc = swf.getAbcList().get(abcIndex).getABC();
methodIndex = abc.bodies.get(bodyIndex).method_info;
}
Main.getMainFrame().getPanel().gotoScriptLine(swf, breakpoint.scriptName, breakpoint.line, classIndex, traitIndex, methodIndex, breakpoint.scriptName.startsWith("#PCODE"));
}
public void refresh() {
DefaultTableModel defaultTableModel = new DefaultTableModel() {
@Override
public boolean isCellEditable(int row, int column) {
return false;
}
};
defaultTableModel.addColumn(translate("breakpoint.scriptName"));
defaultTableModel.addColumn(translate("breakpoint.line"));
defaultTableModel.addColumn(translate("breakpoint.status"));
Map> breakpoints = Main.getDebugHandler().getAllBreakPoints(swf, false);
List newBreakpointList = new ArrayList<>();
for (String scriptName : breakpoints.keySet()) {
for (int line : breakpoints.get(scriptName)) {
newBreakpointList.add(new Breakpoint(scriptName, line));
String status = "unknown";
if (Main.getDebugHandler().isBreakpointInvalid(swf, scriptName, line)) {
status = "invalid";
} else if (Main.getDebugHandler().isBreakpointConfirmed(swf, scriptName, line)) {
status = "confirmed";
}
defaultTableModel.addRow(new Object[]{scriptName, line, translate("breakpoint.status." + status)});
}
}
breakpointList = newBreakpointList;
int selectedRow = table.getSelectedRow();
table.setModel(defaultTableModel);
if (selectedRow >= 0 && selectedRow < defaultTableModel.getRowCount()) {
table.setRowSelectionInterval(selectedRow, selectedRow);
}
table.getColumnModel().getColumn(1).setMaxWidth(50);
table.getColumnModel().getColumn(2).setMaxWidth(75);
}
@Override
public void setVisible(boolean b) {
if (refreshTimer != null) {
refreshTimer.cancel();
refreshTimer = null;
}
if (b) {
refreshTimer = new Timer();
refreshTimer.schedule(new TimerTask() {
@Override
public void run() {
View.execInEventDispatch(new Runnable() {
@Override
public void run() {
refresh();
}
});
}
}, REFRESH_TIMEOUT, REFRESH_TIMEOUT);
}
super.setVisible(b);
}
}