package net.sourceforge.squirrel_sql.client.gui.db;
import net.sourceforge.squirrel_sql.fw.gui.TreeDnDHandler;
import net.sourceforge.squirrel_sql.fw.gui.TreeDnDHandlerCallback;
import net.sourceforge.squirrel_sql.fw.sql.ISQLAlias;
import net.sourceforge.squirrel_sql.fw.util.StringManager;
import net.sourceforge.squirrel_sql.fw.util.StringManagerFactory;
import net.sourceforge.squirrel_sql.fw.util.log.ILogger;
import net.sourceforge.squirrel_sql.fw.util.log.LoggerController;
import net.sourceforge.squirrel_sql.fw.gui.GUIUtils;
import net.sourceforge.squirrel_sql.fw.gui.Dialogs;
import net.sourceforge.squirrel_sql.fw.xml.XMLBeanWriter;
import net.sourceforge.squirrel_sql.fw.xml.XMLBeanReader;
import net.sourceforge.squirrel_sql.fw.id.IIdentifierFactory;
import net.sourceforge.squirrel_sql.client.IApplication;
import net.sourceforge.squirrel_sql.client.ApplicationListener;
import net.sourceforge.squirrel_sql.client.util.ApplicationFiles;
import net.sourceforge.squirrel_sql.client.util.IdentifierFactory;
import javax.swing.*;
import javax.swing.tree.*;
import javax.swing.event.ListDataListener;
import javax.swing.event.ListDataEvent;
import java.awt.*;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseEvent;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.util.*;
import java.io.File;
public class JTreeAliasesListImpl implements IAliasesList, IAliasTreeInterface
{
private static final StringManager s_stringMgr =
StringManagerFactory.getStringManager(JTreeAliasesListImpl.class);
/** Logger for this class. */
private final static ILogger s_log = LoggerController.createLogger(JTreeAliasesListImpl.class);
private TreeDnDHandler _treeDnDHandler;
private static enum PasteMode
{
COPY, CUT;
}
JTree _tree = new JTree()
{
public String getToolTipText(MouseEvent event)
{
return JTreeAliasesListImpl.this.getToolTipText(event); //To change body of overridden methods use File | Settings | File Templates.
}
};
private JScrollPane _comp = new JScrollPane(_tree);
private IApplication _app;
private AliasesListModel _aliasesListModel;
private TreePath[] _pathsToPaste;
private PasteMode _pasteMode;
private boolean _dontReactToAliasAdd = false ;
public JTreeAliasesListImpl(IApplication app, AliasesListModel aliasesListModel)
{
_app = app;
_aliasesListModel = aliasesListModel;
_tree.setRootVisible(false);
DefaultTreeModel treeModel = (DefaultTreeModel) _tree.getModel();
DefaultMutableTreeNode root = (DefaultMutableTreeNode) treeModel.getRoot();
root.removeAllChildren();
_tree.getSelectionModel().setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
_tree.setToolTipText("init");
initRenderer();
initDnD();
_aliasesListModel.addListDataListener(new ListDataListener()
{
public void intervalAdded(ListDataEvent e)
{
onAliasAdded(e);
}
public void intervalRemoved(ListDataEvent e)
{
onAliasRemoved(e);
}
public void contentsChanged(ListDataEvent e)
{
onAliasChanged(e);
}
});
_app.addApplicationListener(new ApplicationListener()
{
public void saveApplicationState()
{
onSaveApplicationState();
}
});
initTree();
}
private void initRenderer()
{
DefaultTreeCellRenderer treeCellRenderer = new DefaultTreeCellRenderer()
{
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus)
{
return modifyRenderer(super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus), value);
}
};
_tree.setCellRenderer(treeCellRenderer);
AbstractAction cancelCutAction = new AbstractAction()
{
public void actionPerformed(ActionEvent actionEvent)
{
if (null != _pathsToPaste && PasteMode.CUT.equals(_pasteMode))
{
_pathsToPaste = null;
_tree.repaint();
}
}
};
KeyStroke escapeStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0);
_tree.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(escapeStroke, "cancelCutAction");
_tree.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(escapeStroke, "cancelCutAction");
_tree.getInputMap(JComponent.WHEN_FOCUSED).put(escapeStroke, "cancelCutAction");
_tree.getActionMap().put("cancelCutAction", cancelCutAction);
}
private Component modifyRenderer(Component component, Object node)
{
JLabel ret = (JLabel) component;
ret.setEnabled(true);
if (null != _pathsToPaste && PasteMode.CUT.equals(_pasteMode))
{
DefaultMutableTreeNode dmtn = (DefaultMutableTreeNode) node;
boolean found = false;
for (TreePath treePath : _pathsToPaste)
{
if(treePath.getLastPathComponent() == dmtn)
{
found = true;
break;
}
}
ret.setEnabled(!found);
ret.setDisabledIcon(ret.getIcon());
}
return component;
}
private void initDnD()
{
TreeDnDHandlerCallback treeDnDHandlerCallback = new TreeDnDHandlerCallback()
{
@Override
public boolean nodeAcceptsKids(DefaultMutableTreeNode selNode)
{
return onNodeAcceptsKids(selNode);
}
@Override
public void dndExecuted() {}
@Override
public ArrayList<DefaultMutableTreeNode> createPasteTreeNodesFromExternalTransfer(DropTargetDropEvent dtde, TreePath targetPath)
{
return null;
}
};
_treeDnDHandler = new TreeDnDHandler(_tree, treeDnDHandlerCallback);
}
private boolean onNodeAcceptsKids(DefaultMutableTreeNode selNode)
{
return false == selNode.isLeaf();
}
private void initTree()
{
DefaultTreeModel treeModel = (DefaultTreeModel) _tree.getModel();
DefaultMutableTreeNode root = (DefaultMutableTreeNode) treeModel.getRoot();
root.removeAllChildren();
File file = new ApplicationFiles().getDatabaseAliasesTreeStructureFile();
if (!readTreeStructureFile(root, file))
{
for (int i = 0; i < _aliasesListModel.size(); i++)
{
root.add(new DefaultMutableTreeNode(_aliasesListModel.get(i)));
}
treeModel.nodeStructureChanged(root);
}
}
/**
* Bug 2942351 (Program doesn't launch)
* Safely performs the reading/parsing of the tree structure from the aliases tree structure file so that
* the tree structure can be ignored if the file is somehow corrupt.
*
* @param root
* the root node of the treemodel for _tree
* @param file
* the file that contains the tree structure xml.
* @return true if the file existed and was parsed successfully; false otherwise.
*/
private boolean readTreeStructureFile(final DefaultMutableTreeNode root, final File file)
{
boolean result = false;
try
{
if (file.exists() && file.length() > 0)
{
XMLBeanReader rdr = new XMLBeanReader();
rdr.load(file);
AliasFolderState rootState = (AliasFolderState) rdr.iterator().next();
applyAliasFolderState(root, rootState);
result = true;
}
}
catch (Exception e)
{
// Throwing a runtime exception here will result in failure to launch the application. Since the tree
// structure can be recovered more easily than all of the user's aliases, we log an error and forget
// about the previous tree structure. Nanoxml will throw a runtime exception for any invalid xml
// that it finds, and we squelch that here with a log message so that launch can proceed.
s_log.error("Unexpected exception while applying Aliases tree structure from file: "
+ file.getAbsolutePath(), e);
}
return result;
}
private void applyAliasFolderState(DefaultMutableTreeNode rootNode, AliasFolderState rootState)
{
DefaultTreeModel treeModel = (DefaultTreeModel) _tree.getModel();
for (AliasFolderState aliasFolderState : rootState.getKids())
{
aliasFolderState.applyNodes(rootNode, _aliasesListModel);
}
ArrayList<SQLAlias> unknownAliases = new ArrayList<SQLAlias>();
for (int i = 0; i < _aliasesListModel.size(); i++)
{
SQLAlias sqlAlias = (SQLAlias) _aliasesListModel.get(i);
if(null == findNode(sqlAlias, rootNode))
{
unknownAliases.add(sqlAlias);
}
}
for (SQLAlias alias : unknownAliases)
{
rootNode.add(new DefaultMutableTreeNode(alias));
}
treeModel.nodeStructureChanged(rootNode);
for (AliasFolderState aliasFolderState : rootState.getKids())
{
aliasFolderState.applyExpansionAndSelection(_tree);
}
}
private void onSaveApplicationState()
{
try
{
DefaultTreeModel dtm = (DefaultTreeModel) _tree.getModel();
DefaultMutableTreeNode root = (DefaultMutableTreeNode) dtm.getRoot();
AliasFolderState state = new AliasFolderState(root, _tree);
XMLBeanWriter wrt = new XMLBeanWriter(state);
wrt.save(new ApplicationFiles().getDatabaseAliasesTreeStructureFile());
}
catch (Exception e)
{
throw new RuntimeException(e);
}
}
private void onAliasChanged(ListDataEvent e)
{
DefaultTreeModel treeModel = (DefaultTreeModel) _tree.getModel();
SQLAlias changedAlias = (SQLAlias) _aliasesListModel.get(e.getIndex0());
DefaultMutableTreeNode root = (DefaultMutableTreeNode) treeModel.getRoot();
DefaultMutableTreeNode node = findNode(changedAlias, root);
treeModel.nodeChanged(node);
}
private DefaultMutableTreeNode findNode(SQLAlias sqlAlias, DefaultMutableTreeNode tn)
{
if(sqlAlias.equals(tn.getUserObject()))
{
return tn;
}
for (int i = 0; i < tn.getChildCount(); i++)
{
DefaultMutableTreeNode ret = findNode(sqlAlias, (DefaultMutableTreeNode) tn.getChildAt(i));
if(null != ret)
{
return ret;
}
}
return null;
}
private void onAliasRemoved(ListDataEvent e)
{
DefaultTreeModel treeModel = (DefaultTreeModel) _tree.getModel();
DefaultMutableTreeNode delNode = findRemovedNode();
DefaultMutableTreeNode nextToSel;
nextToSel = delNode.getNextSibling();
if(null == nextToSel)
{
nextToSel = delNode.getPreviousSibling();
}
DefaultMutableTreeNode parent = (DefaultMutableTreeNode) delNode.getParent();
treeModel.removeNodeFromParent(delNode);
if(null != nextToSel)
{
_tree.setSelectionPath(new TreePath(nextToSel.getPath()));
}
else
{
if(parent != _tree.getModel().getRoot())
{
_tree.setSelectionPath(new TreePath(parent.getPath()));
}
}
}
private DefaultMutableTreeNode findRemovedNode()
{
ArrayList<SQLAlias> buf = new ArrayList<SQLAlias>();
DefaultTreeModel treeModel = (DefaultTreeModel) _tree.getModel();
DefaultMutableTreeNode root = (DefaultMutableTreeNode) treeModel.getRoot();
fillAllAliasesFrom(root, buf);
for (SQLAlias sqlAlias : buf)
{
if(-1 == _aliasesListModel.getIndex(sqlAlias))
{
return findNode(sqlAlias, root);
}
}
return null;
}
private void fillAllAliasesFrom(DefaultMutableTreeNode node, ArrayList<SQLAlias> toFill)
{
if(node.getUserObject() instanceof SQLAlias)
{
toFill.add((SQLAlias) node.getUserObject());
}
else
{
for (int i = 0; i < node.getChildCount(); i++)
{
fillAllAliasesFrom((DefaultMutableTreeNode) node.getChildAt(i), toFill);
}
}
}
private void onAliasAdded(ListDataEvent e)
{
if(_dontReactToAliasAdd)
{
return;
}
SQLAlias newAlias = (SQLAlias) _aliasesListModel.get(e.getIndex0());
DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(newAlias);
DefaultTreeModel treeModel = (DefaultTreeModel) _tree.getModel();
TreePath selPath = _tree.getSelectionPath();
if(null == selPath)
{
DefaultMutableTreeNode root = (DefaultMutableTreeNode) treeModel.getRoot();
root.add(newNode);
}
else
{
DefaultMutableTreeNode selNode = (DefaultMutableTreeNode) selPath.getLastPathComponent();
if(selNode.getUserObject() instanceof SQLAlias)
{
DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode) selPath.getParentPath().getLastPathComponent();
int formerSilblingIx = treeModel.getIndexOfChild(parentNode, selNode);
treeModel.insertNodeInto(newNode, parentNode, formerSilblingIx + 1);
}
else
{
selNode.add(newNode);
}
}
treeModel.nodeStructureChanged((DefaultMutableTreeNode)treeModel.getRoot());
_tree.setSelectionPath(new TreePath(treeModel.getPathToRoot(newNode)));
}
public SQLAlias getSelectedAlias(MouseEvent evt)
{
TreePath path = _tree.getSelectionPath();
if(null == path)
{
return null;
}
if(false == path.getLastPathComponent() instanceof DefaultMutableTreeNode)
{
return null;
}
if(null != evt && false == _tree.getPathBounds(path).contains(evt.getPoint()))
{
// If the mouse wasn't placed on the selected Alias we do nothing.
return null;
}
DefaultMutableTreeNode tn = (DefaultMutableTreeNode) path.getLastPathComponent();
if(false == tn.getUserObject() instanceof ISQLAlias)
{
return null;
}
return (SQLAlias) tn.getUserObject();
}
public void sortAliases()
{
DefaultTreeModel treeModel = (DefaultTreeModel) _tree.getModel();
DefaultMutableTreeNode root = (DefaultMutableTreeNode) treeModel.getRoot();
AliasFolderState state = new AliasFolderState(root, _tree);
state.sort();
root.removeAllChildren();
applyAliasFolderState(root, state);
}
public void requestFocus()
{
_tree.requestFocus();
}
public void deleteSelected()
{
TreePath[] selectionPaths = _tree.getSelectionPaths();
if(1 == selectionPaths.length)
{
DefaultMutableTreeNode selNode = (DefaultMutableTreeNode) selectionPaths[0].getLastPathComponent();
TreeNode parent = selNode.getParent();
if(selNode.getUserObject() instanceof SQLAlias)
{
SQLAlias toDel = (SQLAlias) selNode.getUserObject();
if (Dialogs.showYesNo(_app.getMainFrame(), s_stringMgr.getString("JTreeAliasesListImpl.confirmDelete", toDel.getName())))
{
removeAlias(toDel);
}
}
else
{
if (Dialogs.showYesNo(_app.getMainFrame(), s_stringMgr.getString("JTreeAliasesListImpl.confirmDeleteFolder", selNode.getUserObject())))
{
removeAllAliasesFromNode(selNode);
DefaultTreeModel dtm = (DefaultTreeModel) _tree.getModel();
int indexOfChild = dtm.getIndexOfChild(parent, selNode);
selNode.removeFromParent();
dtm.nodesWereRemoved(parent, new int[]{indexOfChild}, new Object[]{selNode});
//dtm.nodeStructureChanged(parent);
}
}
}
else if(1 < selectionPaths.length)
{
if (Dialogs.showYesNo(_app.getMainFrame(), s_stringMgr.getString("JTreeAliasesListImpl.confirmDeleteMultible")))
{
final HashSet<TreeNode> parentsRemovedFrom = new HashSet<TreeNode>();
for (TreePath selectionPath : selectionPaths)
{
DefaultMutableTreeNode selNode = (DefaultMutableTreeNode) selectionPath.getLastPathComponent();
parentsRemovedFrom.add(selNode.getParent());
if(selNode.getUserObject() instanceof SQLAlias)
{
SQLAlias toDel = (SQLAlias) selNode.getUserObject();
removeAlias(toDel);
}
else
{
removeAllAliasesFromNode(selNode);
selNode.removeFromParent();
}
}
final DefaultTreeModel dtm = (DefaultTreeModel) _tree.getModel();
SwingUtilities.invokeLater(
new Runnable()
{
public void run()
{
for (TreeNode node : parentsRemovedFrom)
{
dtm.nodeStructureChanged(node);
}
}
});
}
}
}
public void modifySelected()
{
TreePath selPath = _tree.getSelectionPath();
if(null == selPath)
{
return;
}
DefaultMutableTreeNode selNode = (DefaultMutableTreeNode) selPath.getLastPathComponent();
if(selNode.getUserObject() instanceof SQLAlias)
{
_app.getWindowManager().showModifyAliasInternalFrame((ISQLAlias) selNode.getUserObject());
}
else
{
String title = s_stringMgr.getString("JTreeAliasesListImpl.EditAliasFolderDlgTitle");
String text = s_stringMgr.getString("JTreeAliasesListImpl.EditAliasFolderDlgText");
EditAliasFolderDlg dlg = new EditAliasFolderDlg(_app.getMainFrame(), title, text, selNode.getUserObject().toString());
GUIUtils.centerWithinParent(dlg);
dlg.setVisible(true);
String folderName = dlg.getFolderName();
if(null == folderName)
{
return;
}
selNode.setUserObject(folderName);
DefaultTreeModel treeModel = (DefaultTreeModel) _tree.getModel();
treeModel.nodeChanged(selNode);
}
}
public boolean isEmpty()
{
return 0 == _aliasesListModel.getSize();
}
private void removeAllAliasesFromNode(DefaultMutableTreeNode selNode)
{
if(selNode.getUserObject() instanceof SQLAlias)
{
SQLAlias toDel = (SQLAlias) selNode.getUserObject();
removeAlias(toDel);
}
else
{
ArrayList<DefaultMutableTreeNode> buf = new ArrayList<DefaultMutableTreeNode>();
for (int i = 0; i < selNode.getChildCount(); i++)
{
buf.add((DefaultMutableTreeNode) selNode.getChildAt(i));
}
for (DefaultMutableTreeNode defaultMutableTreeNode : buf)
{
removeAllAliasesFromNode(defaultMutableTreeNode);
}
}
}
private void removeAlias(SQLAlias toDel)
{
_aliasesListModel.remove(_aliasesListModel.getIndex(toDel));
_app.getDataCache().removeAlias(toDel);
}
public void selectListEntryAtPoint(Point point)
{
TreePath path = _tree.getPathForLocation(point.x, point.y);
if(null != path)
{
_tree.setSelectionPath(path);
}
}
public JComponent getComponent()
{
return _comp;
}
public void addMouseListener(MouseListener mouseListener)
{
_tree.addMouseListener(mouseListener);
}
public void removeMouseListener(MouseListener mouseListener)
{
_tree.removeMouseListener(mouseListener);
}
public String getToolTipText(MouseEvent evt)
{
TreePath path = _tree.getPathForLocation(evt.getPoint().x, evt.getPoint().y);
if(null == path)
{
return null;
}
if(false == path.getLastPathComponent() instanceof DefaultMutableTreeNode)
{
return null;
}
Object userObj = ((DefaultMutableTreeNode) path.getLastPathComponent()).getUserObject();
if(false == userObj instanceof ISQLAlias)
{
return null;
}
return ((ISQLAlias)userObj).getName();
}
public void createNewFolder()
{
String title = s_stringMgr.getString("JTreeAliasesListImpl.NewAliasFolderDlgTitle");
String text = s_stringMgr.getString("JTreeAliasesListImpl.NewAliasFolderDlgText");
EditAliasFolderDlg dlg = new EditAliasFolderDlg(_app.getMainFrame(), title, text, null);
GUIUtils.centerWithinParent(dlg);
dlg.setVisible(true);
String folderName = dlg.getFolderName();
if(null == folderName)
{
return;
}
DefaultTreeModel treeModel = (DefaultTreeModel) _tree.getModel();
TreePath selPath = _tree.getSelectionPath();
DefaultMutableTreeNode newFolder = GUIUtils.createFolderNode(folderName);
if(null != selPath)
{
DefaultMutableTreeNode tn = (DefaultMutableTreeNode) selPath.getLastPathComponent();
if(tn.isLeaf())
{
DefaultMutableTreeNode parent = (DefaultMutableTreeNode) tn.getParent();
int childIndex = parent.getIndex(tn) + 1;
parent.insert(newFolder, childIndex);
treeModel.nodesWereInserted(parent, new int[]{childIndex});
}
else
{
tn.add(newFolder);
treeModel.nodeStructureChanged(tn);
}
}
else
{
DefaultMutableTreeNode root = (DefaultMutableTreeNode) _tree.getModel().getRoot();
int[] childIndices = new int[]{root.getChildCount()};
root.add(newFolder);
treeModel.nodesWereInserted(root, childIndices);
}
//_tree.expandPath(new TreePath(newFolder.getPath()));
_tree.setSelectionPath(new TreePath(newFolder.getPath()));
}
public void cutSelected()
{
_pathsToPaste = _tree.getSelectionPaths();
_pasteMode = PasteMode.CUT;
_tree.repaint();
}
public void pasteSelected()
{
try
{
if (null == _pathsToPaste)
{
return;
}
switch (_pasteMode)
{
case COPY:
execCopyToPaste(_pathsToPaste, _tree.getSelectionPath());
break;
case CUT:
_treeDnDHandler.execCut(_pathsToPaste, _tree.getSelectionPath());
break;
}
}
finally
{
_pathsToPaste = null;
}
}
private void execCopyToPaste(TreePath[] pathsToPaste, TreePath targetPath)
{
DefaultTreeModel dtm = (DefaultTreeModel) _tree.getModel();
DefaultMutableTreeNode[] copiedNodes = new DefaultMutableTreeNode[pathsToPaste.length];
for (int i = 0; i < pathsToPaste.length; i++)
{
copiedNodes[i] = createCopy((DefaultMutableTreeNode) pathsToPaste[i].getLastPathComponent());
}
if (null == targetPath)
{
DefaultMutableTreeNode root = (DefaultMutableTreeNode) dtm.getRoot();
for (int i = 0; i < copiedNodes.length; i++)
{
root.add(copiedNodes[i]);
}
dtm.nodeStructureChanged(root);
}
else
{
DefaultMutableTreeNode selNode = (DefaultMutableTreeNode) targetPath.getLastPathComponent();
if (selNode.isLeaf())
{
DefaultMutableTreeNode parent = (DefaultMutableTreeNode) selNode.getParent();
for (int i = 0; i < copiedNodes.length; i++)
{
parent.insert(copiedNodes[i], parent.getIndex(selNode) + 1);
}
dtm.nodeStructureChanged(parent);
}
else
{
for (int i = 0; i < copiedNodes.length; i++)
{
selNode.add(copiedNodes[i]);
}
dtm.nodeStructureChanged(selNode);
}
}
TreePath[] newSelPaths = new TreePath[copiedNodes.length];
for (int i = 0; i < newSelPaths.length; i++)
{
newSelPaths[i] = new TreePath(copiedNodes[i].getPath());
}
_tree.setSelectionPaths(newSelPaths);
}
private DefaultMutableTreeNode createCopy(DefaultMutableTreeNode nodeToCopy)
{
try
{
if(nodeToCopy.getUserObject() instanceof SQLAlias)
{
SQLAlias source = (SQLAlias) nodeToCopy.getUserObject();
final IIdentifierFactory factory = IdentifierFactory.getInstance();
SQLAlias newAlias = _app.getDataCache().createAlias(factory.createIdentifier());
newAlias.assignFrom(source, false);
try
{
_dontReactToAliasAdd = true;
_app.getDataCache().addAlias(newAlias);
}
finally
{
_dontReactToAliasAdd = false;
}
return new DefaultMutableTreeNode(newAlias);
}
else
{
DefaultMutableTreeNode ret = GUIUtils.createFolderNode((String) nodeToCopy.getUserObject());
for (int i = 0; i < nodeToCopy.getChildCount(); i++)
{
ret.add(createCopy((DefaultMutableTreeNode) nodeToCopy.getChildAt(i)));
}
return ret;
}
}
catch (Exception e)
{
throw new RuntimeException(e);
}
}
public void copyToPasteSelected()
{
_pathsToPaste = _tree.getSelectionPaths();
_pasteMode = PasteMode.COPY;
}
public void collapseAll()
{
for (int i = 0; i < _tree.getRowCount(); i++)
{
_tree.collapseRow(i);
}
}
public void expandAll()
{
for (int i = 0; i < _tree.getRowCount(); i++)
{
_tree.expandRow(i);
}
}
}