/*
 * Created on 29-03-2003
 *
 * To change the template for this generated file go to
 * Window>Preferences>Java>Code Generation>Code and Comments
 */
package net.sf.hibern8ide;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetAdapter;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JEditorPane;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.JToolBar;
import javax.swing.JTree;
import javax.swing.KeyStroke;
import javax.swing.ToolTipManager;
import javax.swing.TransferHandler;
import javax.swing.UIManager;
import javax.swing.border.BevelBorder;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;
import javax.swing.plaf.TabbedPaneUI;
import javax.swing.text.StyledEditorKit;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;

import net.sf.hibern8ide.highlighter.REHqlTypes;
import net.sf.hibern8ide.node.BaseNode;
import net.sf.hibern8ide.node.NodeFactory;
import net.sf.hibernate.HibernateException;
import net.sf.hibernate.Session;
import net.sf.hibernate.SessionFactory;
import net.sf.hibernate.cfg.Configuration;
import net.sf.hibernate.metadata.ClassMetadata;
import net.sf.hibernate.tool.hbm2ddl.SchemaExport;
import net.sf.hibernate.type.EntityType;
import net.sf.hibernate.type.Type;

import org.jgraph.JGraph;
import org.jgraph.graph.ConnectionSet;
import org.jgraph.graph.DefaultEdge;
import org.jgraph.graph.DefaultGraphCell;
import org.jgraph.graph.DefaultGraphModel;
import org.jgraph.graph.DefaultPort;
import org.jgraph.graph.GraphConstants;
import org.jgraph.graph.GraphModel;
import org.jgraph.graph.Port;
import org.pf.joi.Inspector;

/**
 * Main class for Hibern8IDE. Either use directly by runing main() or do a Hibern8IDE.startWith(configuration);
 * 
 * @author max
 *
 */
public class Hibern8IDE {

	static {
		try {
			UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());			
		} catch (Exception e) {

			e.printStackTrace();
		}
	}
	private JMenu namedQueries;
	private QueryPageModel queryPages;
	private JTabbedPane messagesConfigTabs;
	private JPanel statusBar;
	private ErrorListModel errorList;
	private JFrame mainWindow;
	Configuration configuration;
	SessionFactory sf;

    private JEditorPane editor;
	private JSplitPane editorTableSplitPane;
	private JTree tree;
	private History history;
	private Action executeAction;
    private Action executeCriteriaAction;
	private Action forwardHistoryAction;
	private Action backHistoryAction;
	private JGraph graph;
    
    Hibern8IDE() {
		queryPages = new QueryPageModel();
	}

	void reconfig(File configfile, Properties props, List mappings) {
		File hbm = null;
		try {
			configuration = new Configuration();
        
            
			configuration = configuration.setProperties(props);

			if (configfile != null) {
				configuration = configuration.configure(configfile);
			}

			Iterator hbms = mappings.iterator();
		
			while (hbms.hasNext()) {
				hbm = (File) hbms.next();
				configuration = configuration.addFile(hbm.toString());
			}

			initialize();
		} catch (HibernateException e) {
			reportException(e);  //TODO: report which hbm file is doing the error.
		} 

	}

	

	private void initialize() throws HibernateException {
		if (configuration != null) {
			sf = configuration.buildSessionFactory();
			BaseNode node = new NodeFactory(sf).createRootNode();
			tree.setModel(new DefaultTreeModel(node));
			graph.setModel(buildGraphModel(sf));
			graph.setAntiAliased(true);
			buildMenuOfAvailableQueries();                                   
		}
	}

	/**
	 * @param sf2
	 * @return
	 */
	private GraphModel buildGraphModel(SessionFactory sf2) throws HibernateException {
		GraphModel model = new DefaultGraphModel();
		ConnectionSet cs = new ConnectionSet();
		Map attributes = new Hashtable();

		// Styles For Implement/Extend/Aggregation
		Map implementStyle = GraphConstants.createMap();
		GraphConstants.setLineBegin(implementStyle, GraphConstants.ARROW_TECHNICAL);
		GraphConstants.setBeginSize(implementStyle, 10);
		GraphConstants.setDashPattern(implementStyle, new float[] { 3, 3 });
		GraphConstants.setFont(implementStyle, GraphConstants.defaultFont.deriveFont(10));

		Map extendStyle = GraphConstants.createMap();
		GraphConstants.setLineBegin(extendStyle, GraphConstants.ARROW_TECHNICAL);
		GraphConstants.setBeginFill(extendStyle, true);
		GraphConstants.setBeginSize(extendStyle, 10);
		GraphConstants.setFont(extendStyle, GraphConstants.defaultFont.deriveFont(10));

		Map aggregateStyle = GraphConstants.createMap();
		GraphConstants.setLineBegin(aggregateStyle, GraphConstants.ARROW_DIAMOND);
		GraphConstants.setBeginFill(aggregateStyle, true);
		GraphConstants.setBeginSize(aggregateStyle, 6);
		GraphConstants.setLineEnd(aggregateStyle, GraphConstants.ARROW_SIMPLE);
		GraphConstants.setEndSize(aggregateStyle, 8);
		//GraphConstants.setLabelPosition(aggregateStyle, new Point(500, 1200));
		GraphConstants.setFont(aggregateStyle, GraphConstants.defaultFont.deriveFont(10));
		GraphConstants.setLineStyle(aggregateStyle, GraphConstants.STYLE_BEZIER);
		// Model Column
		//DefaultGraphCell rootsf = new DefaultGraphCell(sf2);
		//attributes.put(rootsf, createBounds(20, 100, Color.blue));
		//rootsf.add(new DefaultPort("SessionFactory/Center"));

		Map map = sf2.getAllClassMetadata();
		Map class2Node = new HashMap();
		Map node2class = new HashMap();
		List cms = new ArrayList();
		
		
		cms.addAll(map.values());
		List nodes = new ArrayList();
		for (Iterator iter = cms.iterator(); iter.hasNext();) {
			ClassMetadata element = (ClassMetadata) iter.next();
			DefaultGraphCell dgm = new DefaultGraphCell(element.getMappedClass().getName().substring(element.getMappedClass().getName().lastIndexOf(".")));
			
			dgm.add(new DefaultPort(element.getMappedClass() + "/Center"));
			nodes.add(dgm);
			class2Node.put(element.getMappedClass(), dgm);
			node2class.put(dgm, element);
		}
	
		int count = nodes.size();
		int square = (int) Math.sqrt(count);
		int nodesperrow = count/square;
		
		int row=0;
		int col=0;
		for(int p = 0; p<nodes.size(); p++) {
			col++;
			DefaultGraphCell cell = (DefaultGraphCell) nodes.get(p);
			attributes.put(cell, createBounds(col*150, row*100, Color.blue));
			if(col>=nodesperrow) {
				col = 0;
				row++;
			}
		}
		
		List edges = new ArrayList();
	    int childCount = cms.size();
	    for(int i = 0; i<childCount;i++) {
	    	if((nodes.get(i) instanceof DefaultPort)) continue;
	    	DefaultGraphCell node = (DefaultGraphCell) nodes.get(i);
	    	ClassMetadata md = (ClassMetadata) node2class.get(node);
	    	Port sourcePort = (Port) node.getChildAt(0);
	    	Type[] types = md.getPropertyTypes();
	    	for (int j = 0; j < types.length; j++) {
				Type type = types[j];
				if(type.isEntityType()) {
					DefaultEdge propertyEdge = new DefaultEdge(md.getPropertyNames()[j]);
					EntityType et = (EntityType)type;
					DefaultGraphCell target = (DefaultGraphCell) class2Node.get(et.getPersistentClass());
					attributes.put(propertyEdge, aggregateStyle);
					
					TreeNode targetPort = target.getChildAt(0);
					edges.add(propertyEdge);
					cs.connect(propertyEdge, sourcePort, targetPort);										
				} else {
					//?
				} 
			}
	    }
	    

		// Insert Cells into model
		List allOs = new ArrayList();
		
		allOs.addAll(class2Node.values());
		allOs.addAll(edges);
		Object[] cells = allOs.toArray();
		model.insert(cells, attributes, cs, null, null);
		return model;
	}

	/**
		 * Returns an attributeMap for the specified position and color.
		 */
		public static Map createBounds(int x, int y, Color c) {
			Map map = GraphConstants.createMap();
			GraphConstants.setBounds(map, new Rectangle(x, y, 90, 30));
			GraphConstants.setBorder(map, BorderFactory.createRaisedBevelBorder());
			GraphConstants.setBackground(map, c.darker());
			GraphConstants.setForeground(map, Color.white);
			GraphConstants.setFont(map, GraphConstants.defaultFont.deriveFont(Font.BOLD, 12));
			GraphConstants.setOpaque(map, true);
			return map;
		}
		
	Port getDefaultPort(Object vertex, GraphModel model) {
		// Iterate over all Children
		for (int i = 0; i < model.getChildCount(vertex); i++) {
			// Fetch the Child of Vertex at Index i
			Object child = model.getChild(vertex, i);
			// Check if Child is a Portif (child instanceof Port)
			// Return the Child as a Port
			return (Port) child;
		}
		// No Ports Found
		return null;
	}

	public static void startWith(Configuration c) throws HibernateException {
		Hibern8IDE h8ide = new Hibern8IDE();
		h8ide.setConfiguration(c);
		h8ide.start();
	}

	public static void main(String[] args) throws HibernateException, FileNotFoundException, IOException {

		//          setup the logging
		//TextPanelAppender tpa = new TextPanelAppender(new PatternLayout("%-5p %d [%t]:  %m%n"), "logTextPanel");
		//tpa.setThreshold(Priority.DEBUG);
		//Category cat = Category.getInstance(catName);

		        Hibern8IDE h8 = new Hibern8IDE();
		        h8.start();

		// TODO: just small testing for my internal lazyiness ;)
		/*Configuration cfg = new Configuration();
		Properties p = new Properties();
		p.load(new FileInputStream("U:/projects/os/Hibernate2/src/hibernate.properties"));

		cfg.setProperties(p);
		File f = new File("U:/projects/os/HibernateExt/hibern8ide/src/java/net/sf/hibern8ide/test");
		File[] files = f.listFiles(new FilenameFilter() {
			public boolean accept(File arg0, String arg1) {
				return arg1.endsWith(".hbm.xml");
			}
		});
		for (int i = 0; i < files.length; i++) {
			cfg.addFile(files[i]);
		}

		Hibern8IDE.startWith(cfg);
*/	}

	/**
	 * @param cfg
	 */
	private void setConfiguration(Configuration cfg) {
		configuration = cfg;

	}

	private void start() throws HibernateException {

		mainWindow = setupUI();
		mainWindow.show();
	}

	private JFrame setupUI() throws HibernateException {
		JFrame main = new JFrame("Hibern8IDE");
		main.setIconImage(Toolkit.getDefaultToolkit().getImage(Hibern8IDE.class.getResource("images/hibernate_icon.jpg")));
		Container cont = main.getContentPane();
		messagesConfigTabs = new JTabbedPane();

		createActions();

		Container hqlCommander = buildHQLCommander();
        
		Container configPanel = buildConfigPanel();
		Container messages = buildMessagePanel();
		Container treeView = buildConfigTree();
		Container graphView = buildConfigGraph();
        JTabbedPane treeGraph = new JTabbedPane();
		treeGraph.add("Tree", treeView);
		treeGraph.add("Graph", graphView);

		messagesConfigTabs.addTab("Configuration", configPanel);
		messagesConfigTabs.addTab("HQL Commander", hqlCommander);
		messagesConfigTabs.addTab("Messages", messages);

		JSplitPane spane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, treeGraph, messagesConfigTabs);
		spane.setOneTouchExpandable(true);
		cont.add(spane, BorderLayout.CENTER);

		spane.setDividerLocation(0.3);
		statusBar = new JPanel(new FlowLayout(FlowLayout.LEFT));
		statusBar.add(new JLabel("Ready!"));
		statusBar.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED));
		cont.add(statusBar, BorderLayout.SOUTH);

		JMenu menu = new JMenu("Query");

		menu.add(executeAction);
		menu.add(new RunSchemaExport());
		menu.add(buildMenuOfAvailableQueries());
		
		menu.add(new AbstractAction("Reinitialize") {
			public void actionPerformed(ActionEvent e) {
				
				try {
					initialize();
				} catch (HibernateException e1) {
					e1.printStackTrace();
				}
			}
		});
		
		JMenuBar bar = new JMenuBar();
		bar.add(menu);

		main.setJMenuBar(bar);
		main.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		main.pack();
		main.setExtendedState(JFrame.MAXIMIZED_BOTH);
		//treeTabsSplitPane.setDividerLocation(0.7);
		editorTableSplitPane.setDividerLocation(0.5);
		//treeEditorSplitPane.setDividerLocation(0.7);

		initialize();
		return main;
	}


    private void createActions() {
		executeAction = new ExecuteQuery();
        executeCriteriaAction = new ExecuteJava();

		backHistoryAction = new AbstractAction("Back") {
			public void actionPerformed(ActionEvent e) {
				editor.setText((String) history.back());
			}
		};

		forwardHistoryAction = new AbstractAction("Forward") {
			public void actionPerformed(ActionEvent e) {
				editor.setText((String) history.forward());
			}
		};
	}

	/**
	 * @return
	 */
	private JMenuItem buildMenuOfAvailableQueries() {
		if (namedQueries == null) {
			namedQueries = new JMenu("Named queries");
		}
		namedQueries.removeAll();
		Map map = null;
		if (configuration == null) {
			JMenuItem empty = new JMenuItem("<<No Configuration>>");
			empty.setEnabled(false);
			namedQueries.add(empty);
		} else {
			map = configuration.getNamedQueries();
			Iterator keys = map.keySet().iterator();
			while (keys.hasNext()) {
				String key = (String) keys.next();
				String queryString = (String) map.get(key);
				namedQueries.add(new NamedQueryAction(key, queryString));
			}
		}

		if (map != null && map.size() == 0) {
			namedQueries.add(new JMenuItem("<<No named Queries>>"));
		}

		return namedQueries;
	}

	class NamedQueryAction extends AbstractAction {

		String query;
		NamedQueryAction(String name, String query) {
			super(name);
			this.query = query.trim();
		}

		public void actionPerformed(ActionEvent e) {
			editor.setText(query);
		}

	}

	/**
	 * @return
	 */
	private Container buildConfigGraph() {

		graph = new JGraph();

		return new JScrollPane(graph);

	}

	/**
	 * @return
	 */
	private Container buildConfigTree() {
		tree = new JTree(new DefaultTreeModel(null));
		ToolTipManager.sharedInstance().registerComponent(tree);
		ToolTipManager.sharedInstance().setDismissDelay(Integer.MAX_VALUE);
		tree.setCellRenderer(new MappingsTreeCellRenderer());
		tree.setDragEnabled(true);
		tree.setTransferHandler(new TransferHandler() {
			public int getSourceActions(JComponent c) {
				return COPY;
			}
			protected Transferable createTransferable(JComponent c) {
				if (c instanceof JTree) {
					JTree localTree = (JTree) c;
					TreePath[] paths = localTree.getSelectionPaths();

					if (paths == null || paths.length == 0) {
						return null;
					}

					StringBuffer plainBuf = new StringBuffer();
					StringBuffer htmlBuf = new StringBuffer();

					for (int i = 0; i < paths.length; i++) {
						TreePath path = paths[i];

						BaseNode node = (BaseNode) path.getLastPathComponent();

						String label = node.getHQL();

						plainBuf.append(label + "\n");
					}

					// remove the last newline
					plainBuf.deleteCharAt(plainBuf.length() - 1);
					htmlBuf.append("</ul>\n</body>\n</html>");

					localTree = null;

					return new StringSelection(plainBuf.toString());
				} else {
					return null;
				}
			}
		});
		tree.addMouseListener(new MouseAdapter() {
			public void mouseClicked(MouseEvent e) {
				if (e.getClickCount() == 2) {
					TreePath path = tree.getClosestPathForLocation(e.getX(), e.getY());
					if (path != null && (path.getLastPathComponent() instanceof BaseNode)) {
						executeQuery(((BaseNode) path.getLastPathComponent()).getHQL());
					}
				}
			}
		});
		return new JScrollPane(tree);
	}

	/**
	 * @param node
	 */
	protected void executeQuery(String hql) {
		editor.setText(hql);
	}

	/**
	 * @return
	 */
	private Container buildMessagePanel() {
		JList list = new JList();
		errorList = new ErrorListModel();
		list.setModel(errorList);
		list.setCellRenderer(new ErrorCellRenderer());

		return new JScrollPane(list);
	}

	/**
	 * @return
	 */
	private Container buildConfigPanel() {

		JPanel p = new ConfigurationUI(new ConfigurationUI.ConfigurationUIListener() {
			public void configurationChanged(File configfile, Properties props, List mappingFiles) {
				reconfig(configfile, props, mappingFiles);
			}

		});

		return p;

	}

	/**
	 * @return
	 */
	private Container buildHQLCommander() {

		StyledEditorKit kit = null;
		kit = new REHqlTypes.Kit();

		editor = new JEditorPane();
		editor.setEditorKit(kit);

		new DropTarget(editor, new DropTargetAdapter() {
			public void drop(DropTargetDropEvent e) {
				try {
					DataFlavor stringFlavor = DataFlavor.stringFlavor;
					Transferable tr = e.getTransferable();
					if (e.isDataFlavorSupported(stringFlavor)) {
						String text = (String) tr.getTransferData(stringFlavor);
						e.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
						editor.setText(text);
						e.dropComplete(true);
					} else {
						e.rejectDrop();
					}
				} catch (IOException ioe) {
					ioe.printStackTrace();
				} catch (UnsupportedFlavorException ufe) {
					ufe.printStackTrace();
				}
			}
		});

		//editor.setPreferredSize(new Dimension());
		editor.setMinimumSize(new Dimension(10, 10));
		editor.setMaximumSize(new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE));
		//editor.setTokenMarker(new HQLTokenMarker());
		
		Container panel = buildQueryPageViewer(queryPages);

		editorTableSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, editor, panel);

		editorTableSplitPane.setOneTouchExpandable(true);

		JPanel p = new JPanel(new BorderLayout());

		p.add(editorTableSplitPane, BorderLayout.CENTER);

		JToolBar tbar = new JToolBar();

		tbar.setFloatable(false);

		history = new History();

		final JComboBox box = new JComboBox(history);
		box.setEditable(false);
		box.setAction(new AbstractAction() {
			public void actionPerformed(ActionEvent e) {
				editor.setText((String) box.getSelectedItem());
			}
		});

		tbar.add(executeAction);
        tbar.add(executeCriteriaAction);
		tbar.add(backHistoryAction);
		tbar.add(forwardHistoryAction);
		tbar.add(box);

		p.add(tbar, BorderLayout.NORTH);

		return p;
	}

	/**
	 * @param queryPages
	 * @return
	 */
	private Container buildQueryPageViewer(final QueryPageModel qPages) {
		final JTabbedPane pane = new JTabbedPane();

		final Icon stickyIcon = new ImageIcon(Hibern8IDE.class.getResource("images/pindown.jpg"));
		final Icon nonStickyIcon = new ImageIcon(Hibern8IDE.class.getResource("images/pinup.jpg"));
		qPages.addListDataListener(new ListDataListener() {

			public void intervalAdded(ListDataEvent e) {
				contentsChanged(e);
			}

			public void intervalRemoved(ListDataEvent e) {
				contentsChanged(e);
			}

			public void contentsChanged(ListDataEvent e) {
				int oldIndex = pane.getSelectedIndex();
				pane.removeAll();

				for (Iterator iter = qPages.pages.iterator(); iter.hasNext();) {
					QueryPage element = (QueryPage) iter.next();
					String tabTitle = element.getID() + ":" + element.getQueryString();
					JComponent comp = null;
					Icon icon;
					if (element.isSticky()) {
						icon = stickyIcon;
					} else {
						icon = nonStickyIcon;
					}
					if (element.getExceptions().size() > 0) {
						JTextArea ta = new JTextArea();
						StringBuffer str = new StringBuffer();
						str.append(element.getExceptions().size() + " errors occurred while listing (and calling getPathNames).\n");
						for (Iterator iterator = element.getExceptions().iterator(); iterator.hasNext();) {
                            
							Throwable error = (Throwable) iterator.next();
                            str.append(error + "\n");
                            while(error != null && error.getCause()!=null && error.getCause()!=error) {
                                error = error.getCause();
                                str.append(error + "\n");
                            } 
						}
						ta.setText(str.toString());
						comp = new JScrollPane(ta);

					} else {
						BeanTableModel btm = new BeanTableModel();
						btm.setColumns(element.getPathNames());
						btm.setData(element.getList());
						final JTable table = new JTable(btm);

						// doubleclick and ENTER should do the same.
						final Action sel = new AbstractAction() {
							public void actionPerformed(ActionEvent ae) {
								int row = table.getSelectedRow();
								int col = table.getSelectedColumn();
								if (row >= 0 && col >= 0) {
									Object o = table.getModel().getValueAt(row, col);
									Inspector.basicInspect(o);
								}
							}
						};
						table.registerKeyboardAction(sel, KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), JComponent.WHEN_FOCUSED);
						table.addMouseListener(new MouseAdapter() {
							public void mouseClicked(MouseEvent ae) {
								if (ae.getClickCount() == 2) {
									sel.actionPerformed(null);
								}
							}
						});

						comp = new JScrollPane(table);
					}
					pane.addTab(tabTitle, icon, comp);

				}
				if (oldIndex < pane.getTabCount() && oldIndex >= 0) {
					pane.setSelectedIndex(oldIndex);
				} else if (pane.getTabCount() > 0) {
					pane.setSelectedIndex(pane.getTabCount() - 1);
				}

			}

		});

		pane.addMouseListener(new MouseAdapter() {
			/* (non-Javadoc)
			* @see java.awt.event.MouseAdapter#mouseClicked(java.awt.event.MouseEvent)
			*/
			/* (non-Javadoc)
			* @see java.awt.event.MouseAdapter#mouseReleased(java.awt.event.MouseEvent)
			*/
			public void mouseReleased(MouseEvent e) {
				handleMouse(e, true);
			}

			public void mouseClicked(MouseEvent e) {
				handleMouse(e, false);
			}

			private void handleMouse(final MouseEvent e, boolean toggleSticky) {
				final int i = ((TabbedPaneUI) pane.getUI()).tabForCoordinate(pane, e.getX(), e.getY());

				QueryPage qp = null;

				if (i > -1) {
					qp = qPages.get(i);
				}
				final QueryPage actionQP = qp;

				if (e.isPopupTrigger()) {
					JPopupMenu pop = new JPopupMenu();

					Action removeTab = new AbstractAction("Remove tab") {

						public void actionPerformed(ActionEvent ae) {
							if (i != -1) {
								qPages.remove(i);
							}
						}
					};
					Action hqlToEditor = new AbstractAction("Copy HQL into editor") {
						public void actionPerformed(ActionEvent ae) {
							if (actionQP != null) {
								editor.setText(actionQP.getQueryString());
							}
						}
					};
					pop.add(hqlToEditor);
					pop.add(removeTab);
					pop.show(pane, e.getX(), e.getY());
				} else if (toggleSticky && e.getButton() != MouseEvent.NOBUTTON) {
					if (actionQP != null) {

						actionQP.setSticky(!actionQP.isSticky());
						qPages.update(i);
					} else {
						System.out.println("No QueryPage found at " + i);
					}
				}
			}

		});
		return pane;
	}

	private void reportException(Throwable ef) {
		ef.printStackTrace();
		errorList.addError(ef);
		messagesConfigTabs.setSelectedIndex(messagesConfigTabs.indexOfTab("Messages"));

	}

	private void reportInfo(String string) {
		errorList.addInfo(string);
		messagesConfigTabs.setSelectedIndex(messagesConfigTabs.indexOfTab("Messages"));
	}

	public class RunSchemaExport extends AbstractAction {
		RunSchemaExport() {
			super("Run schema export");
		}

		public void actionPerformed(ActionEvent e) {
			try {
				int result = JOptionPane.showConfirmDialog(mainWindow, "Are you sure you want to (re)create the database schema ? All data can get lost!", "Create database schema", JOptionPane.YES_NO_OPTION);
				if (result == JOptionPane.YES_OPTION) {
					new SchemaExport(configuration).create(false, true);
				}
			} catch (HibernateException ef) {

				reportException(ef);
			}

		}

	}

	/**
	     * @author max
	     *
	     */
	public class ExecuteQuery extends AbstractAction {
		int execcount = 0;
		ExecuteQuery() {
			super("Execute query");
			//initialize the action fields with what you want
			putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, KeyEvent.CTRL_DOWN_MASK));
			putValue(ACTION_COMMAND_KEY, "execQuery");
			putValue(LONG_DESCRIPTION, "Execute the HQL Query");
			putValue(MNEMONIC_KEY, new Integer('q'));
			putValue(SHORT_DESCRIPTION, "Execute Query");
			//putValue(SMALL_ICON, icon);
		}

		public void actionPerformed(ActionEvent e) {

			try {
				if (sf == null) {
					reportInfo("Hibernate not yet configured.");
					return;
				}
				Session session = sf.openSession();
				QueryPage qp = new HQLQueryPage(session, editor.getText());

				qp.setId(++execcount);
				queryPages.add(qp);
				history.addElement(editor.getText());
			} catch (HibernateException e1) {

				reportException(e1);
                
			}
		}
	}

    public class ExecuteJava extends AbstractAction {
        int execcount = 0;
        ExecuteJava() {
            super("Execute Java");
            //initialize the action fields with what you want
            putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, KeyEvent.SHIFT_DOWN_MASK));
            putValue(ACTION_COMMAND_KEY, "execJava");
            putValue(LONG_DESCRIPTION, "Execute Java code and show the result");
            putValue(MNEMONIC_KEY, new Integer('j'));
            putValue(SHORT_DESCRIPTION, "Execute Java");
            //putValue(SMALL_ICON, icon);
        }

        public void actionPerformed(ActionEvent e) {

            try {
                if (sf == null) {
                    reportInfo("Hibernate not yet configured.");
                    return;
                }
                Session session = sf.openSession();
                QueryPage qp = new JavaPage(session, editor.getText());

                qp.setId(++execcount);
                queryPages.add(qp);
                history.addElement(editor.getText());
            } catch (HibernateException e1) {

                reportException(e1);
            }
        }
    }

}
