private void gotoDeclaration(int pos) {
    Reference<Integer> abcIndex = new Reference<>(0);
    Reference<Integer> classIndex = new Reference<>(0);
    Reference<Integer> traitIndex = new Reference<>(0);
    Reference<Boolean> classTrait = new Reference<>(false);
    Reference<Integer> multinameIndexRef = new Reference<>(0);

    if (decompiledTextArea.getPropertyTypeAtPos(
        pos, abcIndex, classIndex, traitIndex, classTrait, multinameIndexRef)) {
      UsageFrame.gotoUsage(
          ABCPanel.this,
          new TraitMultinameUsage(
              getAbcList().get(abcIndex.getVal()).getABC(),
              multinameIndexRef.getVal(),
              classIndex.getVal(),
              traitIndex.getVal(),
              classTrait.getVal(),
              null,
              -1) {});
      return;
    }
    int multinameIndex = decompiledTextArea.getMultinameAtPos(pos);
    if (multinameIndex > -1) {
      List<MultinameUsage> usages = abc.findMultinameDefinition(multinameIndex);

      Multiname m = abc.constants.constant_multiname.get(multinameIndex);
      // search other ABC tags if this is not private multiname
      if (m.namespace_index > 0
          && abc.constants.constant_namespace.get(m.namespace_index).kind
              != Namespace.KIND_PRIVATE) {
        for (ABCContainerTag at : getAbcList()) {
          ABC a = at.getABC();
          if (a == abc) {
            continue;
          }
          int mid = a.constants.getMultinameId(m, false);
          if (mid > 0) {
            usages.addAll(a.findMultinameDefinition(mid));
          }
        }
      }

      // more than one? display list
      if (usages.size() > 1) {
        UsageFrame usageFrame = new UsageFrame(abc, multinameIndex, ABCPanel.this, true);
        usageFrame.setVisible(true);
        return;
      } else if (!usages.isEmpty()) { // one
        UsageFrame.gotoUsage(ABCPanel.this, usages.get(0));
        return;
      }
    }

    int dpos = decompiledTextArea.getLocalDeclarationOfPos(pos, new Reference<>(""));
    if (dpos > -1) {
      decompiledTextArea.setCaretPosition(dpos);
    }
  }
  private boolean hasDeclaration(int pos) {

    SyntaxDocument sd = (SyntaxDocument) decompiledTextArea.getDocument();
    Token t = sd.getTokenAt(pos);
    if (t == null
        || (t.type != TokenType.IDENTIFIER
            && t.type != TokenType.KEYWORD
            && t.type != TokenType.REGEX)) {
      return false;
    }
    Reference<Integer> abcIndex = new Reference<>(0);
    Reference<Integer> classIndex = new Reference<>(0);
    Reference<Integer> traitIndex = new Reference<>(0);
    Reference<Integer> multinameIndexRef = new Reference<>(0);
    Reference<Boolean> classTrait = new Reference<>(false);

    if (decompiledTextArea.getPropertyTypeAtPos(
        pos, abcIndex, classIndex, traitIndex, classTrait, multinameIndexRef)) {
      return true;
    }
    int multinameIndex = decompiledTextArea.getMultinameAtPos(pos);
    if (multinameIndex > -1) {
      if (multinameIndex == 0) {
        return false;
      }
      List<MultinameUsage> usages = abc.findMultinameDefinition(multinameIndex);

      Multiname m = abc.constants.constant_multiname.get(multinameIndex);
      // search other ABC tags if this is not private multiname
      if (m.namespace_index > 0
          && abc.constants.constant_namespace.get(m.namespace_index).kind
              != Namespace.KIND_PRIVATE) {
        for (ABCContainerTag at : getAbcList()) {
          ABC a = at.getABC();
          if (a == abc) {
            continue;
          }
          int mid = a.constants.getMultinameId(m, false);
          if (mid > 0) {
            usages.addAll(a.findMultinameDefinition(mid));
          }
        }
      }

      // more than one? display list
      if (!usages.isEmpty()) {
        return true;
      }
    }

    return decompiledTextArea.getLocalDeclarationOfPos(pos, new Reference<>("")) != -1;
  }
  @Override
  public void updateSearchPos(ABCPanelSearchResult item) {
    ScriptPack pack = item.scriptPack;
    setAbc(pack.abc);
    decompiledTextArea.setScript(pack);
    hilightScript(pack);
    decompiledTextArea.setCaretPosition(0);

    View.execInEventDispatchLater(
        () -> {
          searchPanel.showQuickFindDialog(decompiledTextArea);
        });
  }
  private void saveDecompiledButtonActionPerformed(ActionEvent evt) {
    ScriptPack pack = decompiledTextArea.getScriptLeaf();
    int oldIndex = pack.scriptIndex;
    SWF.uncache(pack);

    try {
      String oldSp = null;
      List<ScriptPack> packs = abc.script_info.get(oldIndex).getPacks(abc, oldIndex, null);
      if (!packs.isEmpty()) {
        oldSp = packs.get(0).getClassPath().toString();
      }

      String as = decompiledTextArea.getText();
      abc.replaceScriptPack(pack, as);
      lastDecompiled = as;
      mainPanel.updateClassesList();

      if (oldSp != null) {
        hilightScript(getSwf(), oldSp);
      }
      setDecompiledEditMode(false);
      reload();
      View.showMessageDialog(
          this,
          AppStrings.translate("message.action.saved"),
          AppStrings.translate("dialog.message.title"),
          JOptionPane.INFORMATION_MESSAGE,
          Configuration.showCodeSavedMessage);
    } catch (AVM2ParseException ex) {
      abc.script_info.get(oldIndex).delete(abc, false);
      decompiledTextArea.gotoLine((int) ex.line);
      decompiledTextArea.markError();
      View.showMessageDialog(
          this,
          AppStrings.translate("error.action.save")
              .replace("%error%", ex.text)
              .replace("%line%", Long.toString(ex.line)),
          AppStrings.translate("error"),
          JOptionPane.ERROR_MESSAGE);
    } catch (CompilationException ex) {
      abc.script_info.get(oldIndex).delete(abc, false);
      decompiledTextArea.gotoLine((int) ex.line);
      decompiledTextArea.markError();
      View.showMessageDialog(
          this,
          AppStrings.translate("error.action.save")
              .replace("%error%", ex.text)
              .replace("%line%", Long.toString(ex.line)),
          AppStrings.translate("error"),
          JOptionPane.ERROR_MESSAGE);
    } catch (Throwable ex) {
      Logger.getLogger(ABCPanel.class.getName()).log(Level.SEVERE, null, ex);
    }
  }
  public void setDecompiledEditMode(boolean val) {
    if (val) {
      lastDecompiled = decompiledTextArea.getText();
    } else {
      decompiledTextArea.setText(lastDecompiled);
    }

    decompiledTextArea.setEditable(val);
    saveDecompiledButton.setVisible(val);
    saveDecompiledButton.setEnabled(false);
    editDecompiledButton.setVisible(!val);
    experimentalLabel.setVisible(!val);
    cancelDecompiledButton.setVisible(val);
    decompiledTextArea.getCaret().setVisible(true);
    decLabel.setIcon(val ? View.getIcon("editing16") : null);
    detailPanel.setVisible(!val);

    decompiledTextArea.ignoreCarret = val;
    decompiledTextArea.requestFocusInWindow();
  }
  private void addTraitButtonActionPerformed(ActionEvent evt) {
    int class_index = decompiledTextArea.getClassIndex();
    if (class_index < 0) {
      return;
    }
    if (newTraitDialog == null) {
      newTraitDialog = new NewTraitDialog();
    }
    int void_type =
        abc.constants.getPublicQnameId(
            "void", true); // abc.constants.forceGetMultinameId(new Multiname(Multiname.QNAME,
    // abc.constants.forceGetStringId("void"), abc.constants.forceGetNamespaceId(new
    // Namespace(Namespace.KIND_PACKAGE, abc.constants.forceGetStringId("")), 0), -1,
    // -1, new ArrayList<Integer>()));
    int int_type =
        abc.constants.getPublicQnameId(
            "int", true); // abc.constants.forceGetMultinameId(new Multiname(Multiname.QNAME,
    // abc.constants.forceGetStringId("int"), abc.constants.forceGetNamespaceId(new
    // Namespace(Namespace.KIND_PACKAGE, abc.constants.forceGetStringId("")), 0), -1,
    // -1, new ArrayList<Integer>()));

    Trait t = null;
    int kind;
    int nskind;
    String name = null;
    boolean isStatic;
    Multiname m;

    boolean again = false;
    loopm:
    do {
      if (again) {
        View.showMessageDialog(
            null,
            AppStrings.translate("error.trait.exists").replace("%name%", name),
            AppStrings.translate("error"),
            JOptionPane.ERROR_MESSAGE);
      }
      again = false;
      if (newTraitDialog.showDialog() != AppDialog.OK_OPTION) {
        return;
      }
      kind = newTraitDialog.getTraitType();
      nskind = newTraitDialog.getNamespaceKind();
      name = newTraitDialog.getTraitName();
      isStatic = newTraitDialog.getStatic();
      m =
          new Multiname(
              Multiname.QNAME,
              abc.constants.getStringId(name, true),
              abc.constants.getNamespaceId(
                  new Namespace(nskind, abc.constants.getStringId("", true)), 0, true),
              0,
              0,
              new ArrayList<>());
      int mid = abc.constants.getMultinameId(m);
      if (mid == 0) {
        break;
      }
      for (Trait tr : abc.class_info.get(class_index).static_traits.traits) {
        if (tr.name_index == mid) {
          again = true;
          break;
        }
      }

      for (Trait tr : abc.instance_info.get(class_index).instance_traits.traits) {
        if (tr.name_index == mid) {
          again = true;
          break;
        }
      }
    } while (again);
    switch (kind) {
      case Trait.TRAIT_GETTER:
      case Trait.TRAIT_SETTER:
      case Trait.TRAIT_METHOD:
        TraitMethodGetterSetter tm = new TraitMethodGetterSetter();
        MethodInfo mi =
            new MethodInfo(
                new int[0],
                void_type,
                abc.constants.getStringId(name, true),
                0,
                new ValueKind[0],
                new int[0]);
        int method_info = abc.addMethodInfo(mi);
        tm.method_info = method_info;
        MethodBody body = new MethodBody();
        body.method_info = method_info;
        body.init_scope_depth = 1;
        body.max_regs = 1;
        body.max_scope_depth = 1;
        body.max_stack = 1;
        body.exceptions = new ABCException[0];
        AVM2Code code = new AVM2Code();
        code.code.add(new AVM2Instruction(0, new GetLocal0Ins(), new int[0]));
        code.code.add(new AVM2Instruction(0, new PushScopeIns(), new int[0]));
        code.code.add(new AVM2Instruction(0, new ReturnVoidIns(), new int[0]));
        body.setCode(code);
        Traits traits = new Traits();
        traits.traits = new ArrayList<>();
        body.traits = traits;
        abc.addMethodBody(body);
        mi.setBody(body);
        t = tm;
        break;
      case Trait.TRAIT_SLOT:
      case Trait.TRAIT_CONST:
        TraitSlotConst ts = new TraitSlotConst();
        ts.type_index = int_type;
        ts.value_kind = ValueKind.CONSTANT_Int;
        ts.value_index = abc.constants.getIntId(0, true);
        t = ts;
        break;
    }
    if (t != null) {
      t.kindType = kind;
      t.name_index = abc.constants.getMultinameId(m, true);
      int traitId;
      if (isStatic) {
        traitId = abc.class_info.get(class_index).static_traits.addTrait(t);
      } else {
        traitId =
            abc.class_info.get(class_index).static_traits.traits.size()
                + abc.instance_info.get(class_index).instance_traits.addTrait(t);
      }
      reload();
      decompiledTextArea.gotoTrait(traitId);
    }
  }
 public void reload() {
   lastDecompiled = "";
   getSwf().clearScriptCache();
   decompiledTextArea.reloadClass();
   detailPanel.methodTraitPanel.methodCodePanel.clear();
 }
  public ABCPanel(MainPanel mainPanel) {

    this.mainPanel = mainPanel;
    setLayout(new BorderLayout());

    decompiledTextArea = new DecompiledEditorPane(this);
    decompiledTextArea.addTextChangedListener(this::decompiledTextAreaTextChanged);

    decompiledTextArea.setLinkHandler(
        new LinkHandler() {

          @Override
          public boolean isLink(Token token) {
            return hasDeclaration(token.length == 1 ? token.start : token.start + 1);
          }

          @Override
          public void handleLink(Token token) {
            gotoDeclaration(token.length == 1 ? token.start : token.start + 1);
          }

          @Override
          public Highlighter.HighlightPainter linkPainter() {
            return decompiledTextArea.linkPainter();
          }
        });

    searchPanel = new SearchPanel<>(new FlowLayout(), this);

    decompiledScrollPane = new JScrollPane(decompiledTextArea);

    JPanel iconDecPanel = new JPanel();
    iconDecPanel.setLayout(new BoxLayout(iconDecPanel, BoxLayout.Y_AXIS));
    JPanel iconsPanel = new JPanel();
    iconsPanel.setLayout(new BoxLayout(iconsPanel, BoxLayout.X_AXIS));

    JButton newTraitButton = new JButton(View.getIcon("traitadd16"));
    newTraitButton.setMargin(new Insets(5, 5, 5, 5));
    newTraitButton.addActionListener(this::addTraitButtonActionPerformed);
    newTraitButton.setToolTipText(AppStrings.translate("button.addtrait"));
    iconsPanel.add(newTraitButton);

    scriptNameLabel = new JLabel("-");
    scriptNameLabel.setAlignmentX(0);
    iconsPanel.setAlignmentX(0);
    decompiledScrollPane.setAlignmentX(0);
    iconDecPanel.add(scriptNameLabel);
    iconDecPanel.add(iconsPanel);
    iconDecPanel.add(decompiledScrollPane);

    JPanel decButtonsPan = new JPanel(new FlowLayout());
    decButtonsPan.setBorder(new BevelBorder(BevelBorder.RAISED));
    decButtonsPan.add(editDecompiledButton);
    decButtonsPan.add(experimentalLabel);
    decButtonsPan.add(saveDecompiledButton);
    decButtonsPan.add(cancelDecompiledButton);

    editDecompiledButton.setMargin(new Insets(3, 3, 3, 10));
    saveDecompiledButton.setMargin(new Insets(3, 3, 3, 10));
    cancelDecompiledButton.setMargin(new Insets(3, 3, 3, 10));

    saveDecompiledButton.addActionListener(this::saveDecompiledButtonActionPerformed);
    editDecompiledButton.addActionListener(this::editDecompiledButtonActionPerformed);
    cancelDecompiledButton.addActionListener(this::cancelDecompiledButtonActionPerformed);

    saveDecompiledButton.setVisible(false);
    cancelDecompiledButton.setVisible(false);
    decButtonsPan.setAlignmentX(0);

    JPanel decPanel = new JPanel(new BorderLayout());
    decPanel.add(searchPanel, BorderLayout.NORTH);
    decPanel.add(iconDecPanel, BorderLayout.CENTER);
    decPanel.add(decButtonsPan, BorderLayout.SOUTH);
    detailPanel = new DetailPanel(this);
    JPanel panB = new JPanel();
    panB.setLayout(new BorderLayout());
    panB.add(decPanel, BorderLayout.CENTER);
    panB.add(decLabel, BorderLayout.NORTH);
    decLabel.setHorizontalAlignment(SwingConstants.CENTER);
    // decLabel.setBorder(new BevelBorder(BevelBorder.RAISED));
    splitPane =
        new JPersistentSplitPane(
            JSplitPane.HORIZONTAL_SPLIT,
            panB,
            detailPanel,
            Configuration.guiAvm2SplitPaneDividerLocationPercent);
    splitPane.setContinuousLayout(true);

    decompiledTextArea.changeContentType("text/actionscript");
    decompiledTextArea.setFont(
        new Font("Monospaced", Font.PLAIN, decompiledTextArea.getFont().getSize()));

    View.addEditorAction(
        decompiledTextArea,
        new AbstractAction() {
          @Override
          public void actionPerformed(ActionEvent e) {
            int multinameIndex = decompiledTextArea.getMultinameUnderCaret();
            if (multinameIndex > -1) {
              UsageFrame usageFrame = new UsageFrame(abc, multinameIndex, ABCPanel.this, false);
              usageFrame.setVisible(true);
            }
          }
        },
        "find-usages",
        AppStrings.translate("abc.action.find-usages"),
        "control U");

    View.addEditorAction(
        decompiledTextArea,
        new AbstractAction() {
          @Override
          public void actionPerformed(ActionEvent e) {
            gotoDeclaration(decompiledTextArea.getCaretPosition());
          }
        },
        "find-declaration",
        AppStrings.translate("abc.action.find-declaration"),
        "control B");

    CtrlClickHandler cch = new CtrlClickHandler();
    decompiledTextArea.addKeyListener(cch);
    decompiledTextArea.addMouseListener(cch);
    decompiledTextArea.addMouseMotionListener(cch);

    navigator = new TraitsList(this);

    navPanel = new JPanel(new BorderLayout());
    JPanel navIconsPanel = new JPanel();
    navIconsPanel.setLayout(new BoxLayout(navIconsPanel, BoxLayout.X_AXIS));
    final JToggleButton sortButton = new JToggleButton(View.getIcon("sort16"));
    sortButton.setMargin(new Insets(3, 3, 3, 3));
    navIconsPanel.add(sortButton);
    navPanel.add(navIconsPanel, BorderLayout.SOUTH);
    navPanel.add(new JScrollPane(navigator), BorderLayout.CENTER);
    sortButton.addActionListener(
        new ActionListener() {
          @Override
          public void actionPerformed(ActionEvent e) {
            navigator.setSorted(sortButton.isSelected());
            navigator.updateUI();
          }
        });

    tabbedPane = new JTabbedPane();
    tabbedPane.addTab(AppStrings.translate("traits"), navPanel);
    add(splitPane, BorderLayout.CENTER);

    JPanel panConstants = new JPanel();
    panConstants.setLayout(new BorderLayout());

    constantTypeList =
        new JComboBox<>(
            new String[] {
              "UINT", "INT", "DOUBLE", "DECIMAL", "STRING", "NAMESPACE", "NAMESPACESET", "MULTINAME"
            });
    constantTable = new JTable();
    if (abc != null) {
      View.autoResizeColWidth(constantTable, new UIntTableModel(abc));
    }
    constantTable.setAutoCreateRowSorter(true);

    final ABCPanel t = this;
    constantTable.addMouseListener(
        new MouseAdapter() {
          @Override
          public void mouseClicked(MouseEvent e) {
            if (e.getClickCount() == 2) {
              if (constantTypeList.getSelectedIndex() == 7) { // MULTINAME
                int rowIndex = constantTable.getSelectedRow();
                if (rowIndex == -1) {
                  return;
                }
                int multinameIndex = constantTable.convertRowIndexToModel(rowIndex);
                if (multinameIndex > 0) {
                  UsageFrame usageFrame = new UsageFrame(abc, multinameIndex, t, false);
                  usageFrame.setVisible(true);
                }
              }
            }
          }
        });
    constantTypeList.addItemListener(this);
    panConstants.add(constantTypeList, BorderLayout.NORTH);
    panConstants.add(new JScrollPane(constantTable), BorderLayout.CENTER);
    tabbedPane.addTab(AppStrings.translate("constants"), panConstants);
  }
 public void clearSwf() {
   this.abc = null;
   constantTable.setModel(new DefaultTableModel());
   navigator.clearAbc();
   decompiledTextArea.clearScript();
 }