/*
Copyright 2012 Jan Ove Saltvedt
This file is part of KBot.
KBot 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.
KBot 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 KBot. If not, see <http://www.gnu.org/licenses/>.
*/
package com.kbotpro.debuggers;
import com.kbotpro.scriptsystem.runnable.Debugger;
import com.kbotpro.scriptsystem.wrappers.IComponent;
import com.kbotpro.scriptsystem.wrappers.Interface;
import com.kbotpro.scriptsystem.events.PaintEventListener;
import com.kbotpro.ui.ReflectionExplorer;
import com.kbotpro.ui.WrappedObject;
import javax.swing.event.TreeSelectionListener;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.table.DefaultTableModel;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.util.*;
import java.util.List;
/**
* Created by IntelliJ IDEA.
* User: Jan Ove Saltvedt
* Date: Oct 20, 2009
* Time: 5:45:27 PM
* To change this template use File | Settings | File Templates.
*/
public class InterfaceDebugger extends Debugger implements PaintEventListener, TreeSelectionListener {
private boolean shallRun;
private InterfaceExplorer UI;
private IComponent currentSelectedIComponent;
/**
* Gets the name shown in the debugs menu
*
* @return String containing name
*/
public String getName() {
return "Interface Debugger";
}
/**
* Is called before the debugger starts to check if it can run.
*
* @return Returns a boolean indicating if the service can be started or not
*/
public boolean canStart() {
return true;
}
/**
* Is called right before the run() gets called
*/
public void onStart() {
shallRun = true;
UI = new InterfaceExplorer();
UI.interfaceTree.addTreeSelectionListener(this);
UI.resetButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
updateTree(new IComponentFilter() {
public boolean includeICompontent(IComponent iComponent, InterfaceExplorer ui) {
return true;
}
});
}
});
UI.filterByTextMenuItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
final String search = JOptionPane.showInputDialog(UI, "Enter the text you want to seach for.", "Filter by text", JOptionPane.PLAIN_MESSAGE).toLowerCase();
if(search == null){
JOptionPane.showMessageDialog(UI, "You got to write something into the box!");
return;
}
updateTree(new IComponentFilter() {
public boolean includeICompontent(IComponent iComponent, InterfaceExplorer ui) {
String text = iComponent.getText();
if (iComponent.getActions() != null && iComponent.getActions().length > 0) {
for (String s : iComponent.getActions()) {
text = text + s;
}
}
text = text + iComponent.getElementName();
return text != null && text.toLowerCase().contains(search);
}
});
}
});
UI.setVisible(true);
updateTree(new IComponentFilter() {
public boolean includeICompontent(IComponent iComponent, InterfaceExplorer ui) {
return true;
}
});
UI.reflectionExlporeMenuItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode) UI.interfaceTree.getLastSelectedPathComponent();
if (node == null){
return;
}
Object nodeObject = node.getUserObject();
if (nodeObject instanceof IComponentNode) {
IComponentNode iComponentNode = (IComponentNode) nodeObject;
IComponent iComponent = iComponentNode.getIComponent();
Object o = iComponent.getClientObject();
ReflectionExplorer reflectionExplorer = new ReflectionExplorer(botEnv);
WrappedObject wrappedObject = new WrappedObject(o, botEnv, null);
DefaultMutableTreeNode defaultMutableTreeNode = new DefaultMutableTreeNode(wrappedObject);
reflectionExplorer.tree.setModel(new DefaultTreeModel(defaultMutableTreeNode));
reflectionExplorer.setVisible(true);
}
}
});
}
private void updateTree(IComponentFilter filter){
DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode("Root");
Interface[] ifaces = interfaces.getInterfaces();
java.util.List<Interface> loadedInterfaces = new LinkedList<Interface>();
for(Interface anInterface: ifaces){
if(anInterface == null){
continue;
}
boolean shallAdd = false;
for(IComponent child: anInterface.getComponents()){
if(filter.includeICompontent(child, UI)){
shallAdd = true;
}
}
if(shallAdd){
loadedInterfaces.add(anInterface);
}
}
if(loadedInterfaces.isEmpty()){
return;
}
fillTree(rootNode, loadedInterfaces);
UI.interfaceTree.setModel(new DefaultTreeModel(rootNode));
UI.interfaceTree.updateUI();
}
private void fillTree(DefaultMutableTreeNode rootNode, java.util.List<Interface> loadedInterfaces){
for(final Interface anInterface: loadedInterfaces){
DefaultMutableTreeNode groupNode = new DefaultMutableTreeNode(new Object(){
@Override
public String toString() {
return "Interface["+anInterface.getID()+"]";
}
});
fillInterfaceComponents(groupNode, anInterface);
rootNode.add(groupNode);
}
}
private void fillInterfaceComponents(DefaultMutableTreeNode groupNode, Interface anInterface) {
IComponent[] components = anInterface.getComponents();
for(int i = 0; i < components.length; i++){
final IComponent iComponent = components[i];
if(iComponent == null){
continue;
}
final int index = i;
DefaultMutableTreeNode node = new DefaultMutableTreeNode(new IComponentNode(){
@Override
public String toString() {
return "["+index+"]";
}
public IComponent getIComponent() {
return iComponent; //To change body of implemented methods use File | Settings | File Templates.
}
});
if(iComponent.hasChildren()){
fillChildren(node, iComponent);
}
groupNode.add(node);
}
}
private void fillChildren(DefaultMutableTreeNode parrentNode, IComponent parrent) {
IComponent[] components = parrent.getChildren();
for(int i = 0; i < components.length; i++){
final IComponent iComponent = components[i];
if(iComponent == null){
continue;
}
final int index = i;
DefaultMutableTreeNode node = new DefaultMutableTreeNode(new IComponentNode(){
@Override
public String toString() {
return "["+index+"]";
}
public IComponent getIComponent() {
return iComponent; //To change body of implemented methods use File | Settings | File Templates.
}
});
if(iComponent.hasChildren()){
fillChildren(node, iComponent);
}
parrentNode.add(node);
}
}
/**
* Is called to pause debugger.
*/
public void pause() {
//To change body of implemented methods use File | Settings | File Templates.
}
/**
* Is called to stop the debugger.
* The debugger is than added to the cleanup queue and thread will be force killed if not deleted within 10 seconds.
*/
public void stop() {
shallRun = false;
UI.setVisible(false);
}
/**
* You should implement the main loop here.
*/
public void run() {
while(shallRun){
sleep(500);
if(!UI.isVisible()){
return;
}
}
}
/**
* Gets called when the client updates it graphics.
* Please do not do anything extremely time consuming in here as it will make the fps go low.
*
* @param g Graphics to paint on
*/
public void onRepaint(Graphics g) {
if(currentSelectedIComponent != null){
Rectangle rectangle = currentSelectedIComponent.getBounds();
g.setColor(Color.orange);
g.drawRect(rectangle.x, rectangle.y, rectangle.width, rectangle.height);
}
}
/**
* Called whenever the value of the selection changes.
*
* @param e the event that characterizes the change.
*/
public void valueChanged(TreeSelectionEvent e) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode) UI.interfaceTree.getLastSelectedPathComponent();
if (node == null){
return;
}
Object nodeObject = node.getUserObject();
if (nodeObject instanceof IComponentNode) {
IComponent face = ((IComponentNode) nodeObject).getIComponent();
currentSelectedIComponent = face;
List<Object[]> data = new LinkedList<Object[]>();
data.add(new Object[]{face.isValid()?"The interface is valid.":"The interface is not valid!"});
data.add(new Object[]{face.isVisible()?"The interface is visible.":"The interface is not visible!"});
data.add(new Object[]{"Screen position:"});
Rectangle rect = face.getBounds();
data.add(new Object[]{" x: " + rect.getX()});
data.add(new Object[]{" y: " + rect.getY()});
data.add(new Object[]{" width: " + rect.getWidth()});
data.add(new Object[]{" height: " + rect.getHeight()});
data.add(new Object[]{""});
data.add(new Object[]{"Relative position"});
data.add(new Object[]{" x: " + face.getRelativeX()});
data.add(new Object[]{" y: " + face.getRelativeY()});
data.add(new Object[]{""});
data.add(new Object[]{"Text:"});
data.add(new Object[]{" Text: " + face.getText()});
data.add(new Object[]{" Text color: " + face.getTextColor()});
data.add(new Object[]{" Texture ID: " + face.getTextureID()});
data.add(new Object[]{""});
String[] actions = face.getActions();
if (actions != null) {
data.add(new Object[]{"Actions :"});
for (String action : actions) {
data.add(new Object[]{" " + action});
}
}
data.add(new Object[]{""});
data.add(new Object[]{"Element"});
data.add(new Object[]{" ID: " + face.getElementID()});
data.add(new Object[]{" Stack size: " + face.getElementStackSize()});
data.add(new Object[]{" Name: " + face.getElementName()});
data.add(new Object[]{" Visible: " + face.isElementVisible()});
data.add(new Object[]{""});
data.add(new Object[]{""});
data.add(new Object[]{"Bot dev info: "});
data.add(new Object[]{"Internal visible array index = "+face.getInternalVisibleArrayIndex()});
data.add(new Object[]{""});
data.add(new Object[]{"Model info: "});
data.add(new Object[]{" Model ID: " + face.getModelID()});
data.add(new Object[]{" Model Zoom: " + face.getModelZoom()});
data.add(new Object[]{" Model Type: " + face.getModelType()});
UI.infoTable.setModel(new DefaultTableModel(
data.toArray(new Object[1][1]),
new String[]{
"Interface information"
}
) {
final Class[] columnTypes = new Class[]{
String.class
};
final boolean[] columnEditable = new boolean[]{
false
};
@Override
public Class<?> getColumnClass(int columnIndex) {
return columnTypes[columnIndex];
}
@Override
public boolean isCellEditable(int rowIndex, int columnIndex) {
return columnEditable[columnIndex];
}
});
} else {
UI.infoTable.setModel(new DefaultTableModel(
new Object[][]{
{"Select an interface."},
},
new String[]{
"Interface information"
}
) {
final Class[] columnTypes = new Class[]{
String.class
};
final boolean[] columnEditable = new boolean[]{
false
};
@Override
public Class<?> getColumnClass(int columnIndex) {
return columnTypes[columnIndex];
}
@Override
public boolean isCellEditable(int rowIndex, int columnIndex) {
return columnEditable[columnIndex];
}
});
}
}
private abstract class IComponentNode{
public abstract IComponent getIComponent();
}
}
interface IComponentFilter {
public boolean includeICompontent(IComponent iComponent, InterfaceExplorer ui);
}