/* * * Perforce/JBuilder Opentool * Copyright (C) 2002 Mark Ackerman * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ package com.dafreels.opentools.actions.ui; import java.io.IOException; import java.util.ArrayList; import java.util.Enumeration; import java.util.HashMap; import java.util.Vector; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Cursor; import java.awt.Frame; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.Point; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.Transferable; import java.awt.datatransfer.UnsupportedFlavorException; import java.awt.dnd.DnDConstants; import java.awt.dnd.DragGestureEvent; import java.awt.dnd.DragGestureListener; import java.awt.dnd.DragGestureRecognizer; import java.awt.dnd.DragSource; import java.awt.dnd.DragSourceContext; import java.awt.dnd.DragSourceDragEvent; import java.awt.dnd.DragSourceDropEvent; import java.awt.dnd.DragSourceEvent; import java.awt.dnd.DragSourceListener; import java.awt.dnd.DropTarget; import java.awt.dnd.DropTargetDragEvent; import java.awt.dnd.DropTargetDropEvent; import java.awt.dnd.DropTargetEvent; import java.awt.dnd.DropTargetListener; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.DefaultListModel; import javax.swing.Icon; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JDialog; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JScrollPane; import javax.swing.JTree; import javax.swing.KeyStroke; import javax.swing.SwingUtilities; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; import javax.swing.text.SimpleAttributeSet; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeCellRenderer; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreePath; import com.borland.primetime.actions.ActionGroup; import com.borland.primetime.actions.ActionToolBar; import com.borland.primetime.ide.Browser; import com.dafreels.opentools.Main; import com.dafreels.opentools.MessageWriter; import com.dafreels.opentools.actions.SubmitAction; import com.dafreels.opentools.properties.PerforceGroup; import com.dafreels.vcs.command.Command; import com.dafreels.vcs.command.CommandTool; import com.dafreels.vcs.command.MessageFormatter; import com.dafreels.vcs.command.PropertyInterface; import com.dafreels.vcs.util.ActionImages; /** * <p>Title: Perforce Open Tool </p> * <p>Description: </p> * <p>Copyright: Copyright (c) 2002</p> * <p>Company: RMA</p> * @author Mark Ackerman * */ public class ChangeListDialog extends JDialog { private static final String SLASH_SLASH = "//"; private static final String DEFAULT = "default"; private static final String CHANGE = "change"; private static final String NEW = "new"; private DnDJTree _changeListTree; private DefaultMutableTreeNode _root; private JPopupMenu _changeNodePopup, _rootNodePopup, _fileNodePopup; private AbstractAction _newChangeListAction, _revertAllAction, // revert all files _revertAction, // revert a single file _submitAction, // submit the changelist _refreshAction, // _refresh the dialog _diffAction, // diff a file _deleteChangeListAction, // delete an empty changelist _propertiesAction, // properties for a file _historyAction, _lockAction, _unlockAction; private JMenuItem _submitActionMenu, _deleteChangeListActionMenu, _lockActionMenu, _unlockActionMenu; private boolean _hasPacked = false; /** * * <p>Title: Perforce Opentool</p> * <p>Description: </p> * <p>Copyright: Copyright (c) 2003</p> * <p>Company: </p> * @author Mark Ackerman * @version 1.0 */ private class ChangeNode extends DefaultMutableTreeNode { private String _description; private String _toString; ChangeNode(String change) { super(change); } public String getChangeNumber() { String changeDesc = (String)getUserObject(); if ( changeDesc.startsWith(DEFAULT)) { return "default"; } else if ( changeDesc.startsWith(CHANGE)) { int idx = changeDesc.indexOf(" "); return changeDesc.substring(idx+1); } return ""; } public void setDescription(String desc) { _description = desc; _toString = null; } public String toString() { if ( _toString == null ) { if ( _description != null && _description.length() > 0 ) { _toString = (String)getUserObject()+ " {"+_description+"}"; } else { _toString = (String)getUserObject(); } } return _toString; } } /** * * <p>Title: Perforce Opentool</p> * <p>Description: </p> * <p>Copyright: Copyright (c) 2003</p> * <p>Company: </p> * @author Mark Ackerman * @version 1.0 */ private class ChangeFileNode extends DefaultMutableTreeNode { private ChangeInfo _changeInfo; ChangeFileNode(ChangeInfo info) { super(info); _changeInfo = info; } public String toString() { return _changeInfo.toString(); } public boolean isLocked() { return _changeInfo.isLocked(); } public boolean isAdd() { return _changeInfo.isAdd(); } public boolean isEdit() { return _changeInfo.isEdit(); } public boolean isDelete() { return _changeInfo.isDelete(); } public boolean getIntegrate() { return _changeInfo.getIntegrate(); } public boolean getAllowsChildren() { return false; } public String getFile() { return _changeInfo.getFile(); } public String getFileNoRevision() { String file = _changeInfo.getFile(); int idx = file.indexOf("#"); if ( idx > -1 ) { return file.substring(0, idx); } idx = file.indexOf("@"); if ( idx > -1 ) { return file.substring(0, idx); } return file; } } /** * Renderer for the ChangeListTree * * @author Mark Ackerman * */ public class ChangeListTreeRenderer extends DefaultTreeCellRenderer { private HashMap _iconMap = new HashMap(); private TreeIcon _icon; private Icon _rootIcon; private Icon _folderIcon; public ChangeListTreeRenderer() { _icon = new TreeIcon(false); _rootIcon = ActionImages.P4_CHANGELIST_ROOT; _folderIcon = ActionImages.P4_CHANGELIST_NODE; } /** * Configures the renderer based on the passed in components. * The value is set from messaging the tree with * <code>convertValueToText</code>, which ultimately invokes * <code>toString</code> on <code>value</code>. * The foreground color is set based on the selection and the icon * is set based on on leaf and expanded. */ public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) { String stringValue = tree.convertValueToText(value, sel, expanded, leaf, row, hasFocus); this.hasFocus = hasFocus; setText(stringValue); if(sel) setForeground(getTextSelectionColor()); else setForeground(getTextNonSelectionColor()); if (!tree.isEnabled()) { setEnabled(false); if (leaf) { setDisabledIcon(getLeafIcon(value, row)); } else if (expanded) { setDisabledIcon(getOpenIcon(value, row)); } else { setDisabledIcon(getClosedIcon(value, row)); } } else { setEnabled(true); if (leaf) { setIcon(getLeafIcon(value, row)); } else if (expanded) { setIcon(getOpenIcon(value, row)); } else { setIcon(getClosedIcon(value, row)); } } setComponentOrientation(tree.getComponentOrientation()); selected = sel; return this; } Icon getLeafIcon(Object value, int row) { if ( value == null ) return super.getLeafIcon(); Icon icon = getIcon(value, row); if ( icon == null ) return super.getLeafIcon(); return icon; } Icon getOpenIcon(Object value, int row) { if ( value == null ) return super.getOpenIcon(); Icon icon = getIcon(value, row); if ( icon == null ) return super.getOpenIcon(); return icon; } Icon getClosedIcon(Object value, int row) { if ( value == null ) return super.getClosedIcon(); Icon icon = getIcon(value, row); if ( icon == null ) return super.getClosedIcon(); return icon; } Icon getIcon(Object value, int row) { Class cls = value.getClass(); Icon icon = null; if ( value == _root ) { return _rootIcon; } if ( value instanceof ChangeFileNode ) { ChangeFileNode cn = (ChangeFileNode)value; _icon.setAdd(cn.isAdd()); _icon.setDelete(cn.isDelete()); _icon.setLocked(cn.isLocked()); _icon.setEdit(cn.isEdit()); _icon.setIntegrate(cn.getIntegrate()); return _icon; } return _folderIcon; } } /** * create a new ChangeList dialog * @param parent the Frame owner * @param modal true for a modal dialog */ public ChangeListDialog(Frame parent, boolean modal) { super(parent, modal); getContentPane().setLayout(new BorderLayout()); JPanel centerPanel = new JPanel(new GridBagLayout()); getContentPane().add(centerPanel, BorderLayout.CENTER); createToolbar(); setTitle("Pending ChangeLists"); _root = new DefaultMutableTreeNode("Pending ChangeLists (client "+Main.m_props.getClientSpec()+")"); _changeListTree = new DnDJTree(_root, parent); _changeListTree.setCellRenderer(new ChangeListTreeRenderer()); GridBagConstraints gbc = new GridBagConstraints(); gbc.gridx = 0; gbc.gridy = 0; gbc.gridwidth = 1; gbc.anchor = GridBagConstraints.NORTHWEST; gbc.fill = GridBagConstraints.BOTH; gbc.insets = new Insets(5,5,0,5); gbc.weightx = 1.0; gbc.weighty = 1.0; centerPanel.add(new JScrollPane(_changeListTree), gbc); JButton okButton = new JButton("Close"); gbc = new GridBagConstraints(); gbc.gridx = 0; gbc.gridy = 1; gbc.gridwidth = 1; gbc.anchor = GridBagConstraints.SOUTH; gbc.fill = GridBagConstraints.NONE; gbc.insets = new Insets(5,5,0,5); centerPanel.add(okButton, gbc); okButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { setVisible(false); dispose(); } }); createFileNodePopup(); _changeListTree.addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent e) { if ( e.isPopupTrigger() || SwingUtilities.isRightMouseButton(e)) { TreePath path = _changeListTree.getSelectionPath(); if ( path == null ) { path = _changeListTree.getPathForLocation(e.getX(), e.getY()); if ( path == null ) { return; } _changeListTree.setSelectionPath(path); } DefaultMutableTreeNode node = (DefaultMutableTreeNode)path.getLastPathComponent(); JPopupMenu popup = null; if ( node == _root ) { if ( _rootNodePopup == null ) { _rootNodePopup = createRootNodePopup(); } popup = _rootNodePopup; } else if ( node instanceof ChangeNode ) { if ( _changeNodePopup == null ) { _changeNodePopup = createChangeNodePopup(); } popup = _changeNodePopup; boolean hasChildren = node.getChildCount() == 0; _submitActionMenu.setVisible(!hasChildren); _deleteChangeListActionMenu.setVisible(hasChildren); } else if ( node instanceof ChangeFileNode ) { if ( _fileNodePopup == null ) { _fileNodePopup = createFileNodePopup(); } popup = _fileNodePopup; } if ( popup == null ) { return; } popup.show(_changeListTree, e.getX(), e.getY()); } } }); _changeListTree.addTreeSelectionListener(new TreeSelectionListener() { public void valueChanged(TreeSelectionEvent e) { TreePath path = e.getNewLeadSelectionPath(); if ( path == null ) { _diffAction.setEnabled(false); _submitAction.setEnabled(false); return; } Object obj = path.getLastPathComponent(); if ( obj instanceof ChangeFileNode ) { _diffAction.setEnabled(true); _submitAction.setEnabled(true); ChangeFileNode cfnode = (ChangeFileNode)obj; if ( cfnode.isLocked() ) { _lockActionMenu.setVisible(false); _unlockActionMenu.setVisible(true); } else { _lockActionMenu.setVisible(true); _unlockActionMenu.setVisible(false); } } else if ( obj instanceof ChangeNode ) { _diffAction.setEnabled(false); _submitAction.setEnabled(true); } else { _diffAction.setEnabled(false); _submitAction.setEnabled(false); } } }); getRootPane().registerKeyboardAction( new ActionListener() { public void actionPerformed(ActionEvent e) { refreshDialog(); } }, KeyStroke.getKeyStroke(KeyEvent.VK_F5, 0, true), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); } private void createToolbar() { ActionGroup ag = new ActionGroup(); ag.add(getRefreshAction()); ag.add(getRevertAllFilesAction()); ag.add(getNewChangeListAction()); ag.add(getSubmitChangeListAction()); ag.add(getDiffAction()); ActionToolBar toolbar = new ActionToolBar(this, ag); /* JToolBar toolbar = new JToolBar(); toolbar.add(getRefreshAction()); toolbar.addSeparator(); toolbar.add(getRevertFilesAction()); toolbar.addSeparator(); toolbar.add(getSubmitChangeListAction()); toolbar.addSeparator(); toolbar.add(getDiffAction()); */ getContentPane().add(toolbar,BorderLayout.NORTH); } private JPopupMenu createRootNodePopup() { JPopupMenu menu = new JPopupMenu(); menu.add(getNewChangeListAction()); menu.add(getRefreshAction()); return menu; } private JPopupMenu createChangeNodePopup() { JPopupMenu menu = new JPopupMenu(); _submitActionMenu = new JMenuItem(getSubmitChangeListAction()); menu.add(_submitActionMenu); _deleteChangeListActionMenu = new JMenuItem(getDeleteChangeListAction()); menu.add(_deleteChangeListActionMenu); menu.addSeparator(); menu.add(getRevertAllFilesAction()); menu.addSeparator(); menu.add(getNewChangeListAction()); menu.add(getRefreshAction()); return menu; } private JPopupMenu createFileNodePopup() { JPopupMenu popupMenu = new JPopupMenu(); popupMenu.add(getSubmitChangeListAction()); popupMenu.addSeparator(); popupMenu.add(getDiffAction()); _lockActionMenu = new JMenuItem(getLockAction()); _lockActionMenu.setVisible(false); popupMenu.add(_lockActionMenu); _unlockActionMenu = new JMenuItem(getUnlockAction()); _unlockActionMenu.setVisible(false); popupMenu.add(_unlockActionMenu); popupMenu.add(getHistoryAction()); popupMenu.add(getPropertiesAction()); popupMenu.addSeparator(); popupMenu.add(getRevertFileAction()); return popupMenu; } /** * get the Revert All Files Action * @return */ private Action getRevertAllFilesAction() { if ( _revertAllAction == null ) { _revertAllAction = new AbstractAction("Revert Unchanged Files") { public void actionPerformed(ActionEvent e) { revertFiles(); } }; _revertAllAction.putValue(Action.SMALL_ICON, ActionImages.P4_REVERT); _revertAllAction.putValue(Action.SHORT_DESCRIPTION, "Revert Selected Files"); } return _revertAllAction; } private Action getRevertFileAction() { if ( _revertAction == null ) { _revertAction = new AbstractAction("Revert") { public void actionPerformed(ActionEvent e) { revertFiles(); } }; _revertAction.putValue(Action.SMALL_ICON, ActionImages.P4_REVERT); _revertAction.putValue(Action.SHORT_DESCRIPTION, "Revert Changes to File"); } return _revertAction; } private Action getLockAction() { if ( _lockAction == null ) { _lockAction = new AbstractAction("Lock") { public void actionPerformed(ActionEvent e) { lockFiles(); } }; _lockAction.putValue(Action.SMALL_ICON, ActionImages.P4_LOCK); _lockAction.putValue(Action.SHORT_DESCRIPTION, "Lock File at Depot"); } return _lockAction; } private Action getUnlockAction() { if ( _unlockAction == null ) { _unlockAction = new AbstractAction("Unlock") { public void actionPerformed(ActionEvent e) { unlockFiles(); } }; _unlockAction.putValue(Action.SMALL_ICON, ActionImages.P4_UNLOCK); _unlockAction.putValue(Action.SHORT_DESCRIPTION, "Unlock File at Depot"); } return _unlockAction; } /** * * @return */ private Action getHistoryAction() { if ( _historyAction == null ) { _historyAction = new AbstractAction("Revision History") { public void actionPerformed(ActionEvent e) { history(); } }; _historyAction.putValue(Action.SMALL_ICON, ActionImages.P4_HISTORY); _historyAction.putValue(Action.SHORT_DESCRIPTION, "Show Revision History"); } return _historyAction; } /** * * @return */ private Action getPropertiesAction() { if ( _propertiesAction == null ) { _propertiesAction = new AbstractAction("Properties...") { public void actionPerformed(ActionEvent e) { properties(); } }; _propertiesAction.putValue(Action.SHORT_DESCRIPTION, "Display File Information"); } return _propertiesAction; } /** * get the Diff Action * @return */ private Action getDiffAction() { if ( _diffAction == null ) { _diffAction = new AbstractAction("Diff") { public void actionPerformed(ActionEvent e) { diff(); } }; // not yet implemented _diffAction.putValue(Action.SMALL_ICON, ActionImages.P4_DIFF); _diffAction.putValue(Action.SHORT_DESCRIPTION, "Diff vs. Depot File"); } return _diffAction; } /** * get the Submit Changelist Action * @return */ private Action getSubmitChangeListAction() { if ( _submitAction == null ) { _submitAction = new AbstractAction("Submit...") { public void actionPerformed(ActionEvent e) { TreePath[] paths = _changeListTree.getSelectionPaths(); if ( paths == null ) { return; } ArrayList files = new ArrayList(paths.length); ChangeNode changeNode = null; for (int i = 0; i < paths.length; i++ ) { Object obj = paths[i].getLastPathComponent(); if ( obj instanceof ChangeFileNode ) { if ( changeNode != null ) { if ( ((ChangeFileNode)obj).getParent() != changeNode) { continue; } } changeNode = (ChangeNode)((ChangeFileNode)obj).getParent(); files.add(((ChangeFileNode)obj).getFileNoRevision()); } else if ( obj instanceof ChangeNode ) { if ( changeNode == null ) { changeNode = (ChangeNode)obj; } } else { continue; } } if ( changeNode != null ) { String changeNumber = changeNode.getChangeNumber(); if ( ChangeListDialog.DEFAULT.equals(changeNumber)) { changeNumber = null; } if ( new SubmitAction().submit(changeNumber, files)) { refreshDialog(); } } } }; _submitAction.putValue(Action.SMALL_ICON, ActionImages.P4_SUBMIT); _submitAction.putValue(Action.SHORT_DESCRIPTION, "Submit the ChangeList"); } return _submitAction; } private Action getDeleteChangeListAction() { if ( _deleteChangeListAction == null ) { _deleteChangeListAction = new AbstractAction("Delete Empty Changelist") { public void actionPerformed(ActionEvent e) { TreePath path = _changeListTree.getSelectionPath(); Object obj = path.getLastPathComponent(); if ( obj instanceof ChangeNode ) { ChangeNode cnode = (ChangeNode)obj; if ( cnode.getChildCount() == 0 ) { CommandTool.runCommand(new Command("change -d " + cnode.getChangeNumber()), Main.m_props); MessageWriter.outputErrorMessages(MessageFormatter.getInstance()); refreshDialog(); } } } }; } return _deleteChangeListAction; } private Action getNewChangeListAction() { if ( _newChangeListAction == null ) { _newChangeListAction = new AbstractAction("New ChangeList") { public void actionPerformed(ActionEvent e) { newChangeList(); refreshDialog(); } }; _newChangeListAction.putValue(Action.SMALL_ICON, ActionImages.P4_CHANGELIST_NEW); _newChangeListAction.putValue(Action.SHORT_DESCRIPTION, "Create a new ChangeList"); } return _newChangeListAction; } private void newChangeList() { CommandTool.runCommand(new Command(Command.CHANGELIST_NEW), Main.m_props); MessageWriter.outputErrorMessages(MessageFormatter.getInstance()); java.util.Vector data = new java.util.Vector(); java.util.Vector files = new java.util.Vector(); java.util.Vector dataInteg = new java.util.Vector(); //Create a file list String tmp = ""; //while(cl.indexOf(newLine) != -1) //Get a list of files int idx; while( (tmp = MessageFormatter.getNextMessage()) != null) { data = new java.util.Vector(); //tmp = cl.substring(0, cl.indexOf(newLine)); //cl = cl.substring(cl.indexOf(newLine) + newLine.length()); if((idx = tmp.lastIndexOf("//")) != -1) { tmp = tmp.substring(idx);//tmp.lastIndexOf("//")); } if((idx = tmp.indexOf("#")) > -1) { tmp = tmp.substring(0, idx); //tmp.indexOf("#")); } if(!dataInteg.contains(tmp) && tmp.indexOf("//") == 0) { data.add(new Boolean("true")); tmp = tmp.trim(); data.add(tmp); dataInteg.add(tmp); files.add(data); } } dataInteg = null; SubmitDialog dialog = new SubmitDialog((Frame)getParent(), "Perforce Change Specification", true); dialog.showSubmit(null, files, null); if(!dialog.getState()) { return; } String newLine = System.getProperty("line.separator"); StringBuffer changeList = new StringBuffer("Change: "); changeList.append(dialog.getChangeNumber()); changeList.append(newLine); changeList.append(" "+newLine); changeList.append("Client: "+PerforceGroup.CLIENTSPEC.getValue(Browser.getActiveBrowser().getActiveProject())+newLine); changeList.append(" "+newLine); changeList.append("User: "+PerforceGroup.USERNAME.getValue(Browser.getActiveBrowser().getActiveProject())+newLine); changeList.append(" "+newLine); changeList.append("Status: "); changeList.append(dialog.getStatus()); changeList.append(newLine); changeList.append(" "+newLine); changeList.append("Description: "+newLine); //Add the description given by the user changeList.append(dialog.getDescription()); changeList.append(" "+newLine); changeList.append("Files: "+newLine); //Get the files list from the dialog files = dialog.getFiles(); Vector v; //Add the files to the changeList for(int i = 0; i < files.size(); i++) { v = (Vector) files.elementAt(i); changeList.append("\t"+v.elementAt(1)+newLine); } //Create the command StringBuffer sb = new StringBuffer("change -i"); //Run the command CommandTool.runCommand(new Command(sb.toString()), true, true, false, Main.m_props); while(!CommandTool.isStreamReady()) { //do nothing } CommandTool.writeToOut(changeList.toString()); //Output the p4 messages MessageWriter.outputErrorMessages(MessageFormatter.getInstance()); if ( Main.m_props.showOutput()) { MessageWriter.outputMessages(MessageFormatter.getInstance()); } refreshDialog(); } private Action getRefreshAction() { if ( _refreshAction == null ) { _refreshAction = new AbstractAction("Refresh F5") { public void actionPerformed(ActionEvent e) { refreshDialog(); } }; _refreshAction.putValue(Action.SMALL_ICON, ActionImages.P4_REFRESH); _refreshAction.putValue(Action.SHORT_DESCRIPTION, "Refresh"); } return _refreshAction; } /** * rerun the opened command */ protected void refreshDialog() { try { setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); //Run the command CommandTool.runCommand(new Command(Command.OPENED), Main.m_props); //Output the p4 messages MessageWriter.outputErrorMessages(MessageFormatter.getInstance()); fillForm(MessageFormatter.getInstance()); } finally { setCursor(Cursor.getDefaultCursor()); } } private void history() { TreePath[] paths = _changeListTree.getSelectionPaths(); if ( paths == null || paths.length == 0 ) { return; } DefaultMutableTreeNode node; for (int i = 0; i < paths.length; i++ ) { node = (DefaultMutableTreeNode)paths[i].getLastPathComponent(); if ( node instanceof ChangeFileNode ) { fileHistory(((ChangeFileNode)node).getFile()); } } } private void fileHistory(String fileName) { Command cmd = new Command(Command.FILEHISTORY); cmd.addPath(fileName); CommandTool.runCommand(cmd, Main.m_props); HistoryDialog dialog = new HistoryDialog(Browser.getActiveBrowser(), "File Information", false); dialog.fillForm(fileName, MessageFormatter.getInstance()); dialog.setVisible(true); } /** * get the file properties for the selected files */ private void properties() { TreePath[] paths = _changeListTree.getSelectionPaths(); if ( paths == null || paths.length == 0 ) { return; } DefaultMutableTreeNode node; for (int i = 0; i < paths.length; i++ ) { node = (DefaultMutableTreeNode)paths[i].getLastPathComponent(); if ( node instanceof ChangeFileNode ) { fileProperties(((ChangeFileNode)node).getFile()); } } } /** * run the fstat command for the file * @param file */ private void fileProperties(String file) { Command cmd = new Command(Command.STATUS); cmd.addPath(file); CommandTool.runCommand(cmd, Main.m_props); StatusDialog dialog = new StatusDialog(Browser.getActiveBrowser(), "File Information", false); dialog.fillForm(MessageFormatter.getInstance()); dialog.setVisible(true); } /** * diff the selected files */ private void diff() { TreePath[] paths = _changeListTree.getSelectionPaths(); if ( paths == null || paths.length == 0 ) { return; } DefaultMutableTreeNode node; for (int i = 0; i < paths.length; i++ ) { node = (DefaultMutableTreeNode)paths[i].getLastPathComponent(); if ( node instanceof ChangeFileNode ) { diffFile(((ChangeFileNode)node).getFile()); } } } /** * run the diff command for the file * @param file */ private void diffFile(String file) { // really should let the DiffAction handle this Command cmd = new Command(Command.DIFF); cmd.addPath(file); //StringBuffer sb = new StringBuffer("diff -f "); //sb.append(file); //Run the command //CommandTool.runCommand(sb.toString(), Main.m_props); CommandTool.runCommand(cmd, Main.m_props); //Output the p4 messages MessageWriter.outputErrorMessages(MessageFormatter.getInstance()); MessageWriter.outputMessages(MessageFormatter.getInstance()); } /** * revert the selected files */ private void revertFiles() { TreePath[] paths = _changeListTree.getSelectionPaths(); if ( paths == null || paths.length == 0 ) { return; } DefaultMutableTreeNode node; ArrayList files = new ArrayList(paths.length); for (int i = 0; i < paths.length; i++ ) { node = (DefaultMutableTreeNode)paths[i].getLastPathComponent(); if ( node instanceof ChangeFileNode ) { files.add(((ChangeFileNode)node).getFileNoRevision()); } } if ( files.size() > 0 ) { revertFiles(files); } } /** * revert the files in the List files * @param files the files to revert */ private void revertFiles(java.util.List files) { Command cmd = new Command(Command.REVERT); cmd.addPaths(files); java.util.List paths = cmd.getPaths(); if ( paths.size() == 0 ) { return; } StringBuffer buf = new StringBuffer(200); java.util.Iterator i = paths.iterator(); while (i.hasNext() ) { buf.append(i.next()); if ( i.hasNext()) { buf.append("<br>"); } } int opt = JOptionPane.showConfirmDialog(this, "<html>Confirm Reverting of the following files:<br><font=+0 Color=blue>"+ buf.toString()+"</font></html>", "Changes will be lost", JOptionPane.OK_CANCEL_OPTION); if ( opt == JOptionPane.OK_OPTION ) { CommandTool.runCommand(cmd, Main.m_props); MessageWriter.outputErrorMessages(MessageFormatter.getInstance()); MessageWriter.outputMessages(MessageFormatter.getInstance()); refreshDialog(); } } /** * */ private void unlockFiles() { TreePath[] paths = _changeListTree.getSelectionPaths(); if ( paths == null || paths.length == 0 ) { return; } DefaultMutableTreeNode node; ChangeFileNode cfNode; ArrayList files = new ArrayList(paths.length); for (int i = 0; i < paths.length; i++ ) { node = (DefaultMutableTreeNode)paths[i].getLastPathComponent(); if ( node instanceof ChangeFileNode ) { cfNode = (ChangeFileNode)node; if ( cfNode.isLocked() ) { files.add(cfNode.getFileNoRevision()); } } } if ( files.size() > 0 ) { lockFiles(files, Command.UNLOCK); } } /** * lock the selected files that aren't locked */ private void lockFiles() { TreePath[] paths = _changeListTree.getSelectionPaths(); if ( paths == null || paths.length == 0 ) { return; } DefaultMutableTreeNode node; ChangeFileNode cfNode; ArrayList files = new ArrayList(paths.length); for (int i = 0; i < paths.length; i++ ) { node = (DefaultMutableTreeNode)paths[i].getLastPathComponent(); if ( node instanceof ChangeFileNode ) { cfNode = (ChangeFileNode)node; if ( !cfNode.isLocked() ) { files.add(cfNode.getFileNoRevision()); } } } if ( files.size() > 0 ) { lockFiles(files, Command.LOCK); } } private void lockFiles(java.util.List files, String perforceCmd) { Command cmd = new Command(perforceCmd); cmd.addPaths(files); CommandTool.runCommand(cmd, Main.m_props); MessageWriter.outputErrorMessages(MessageFormatter.getInstance()); MessageWriter.outputMessages(MessageFormatter.getInstance()); refreshDialog(); } /** * fill this dialog with the contents of formatter * @param formatter the output from the <code>opened</code> command */ public void fillForm(MessageFormatter formatter) { String message, arg; SimpleAttributeSet attrs; PropertyInterface props = Main.m_props; DefaultListModel model = new DefaultListModel(); formatter.getNextMessage(); // throw away first line MessageWriter.outputErrorMessages(MessageFormatter.getInstance()); int idx; ChangeFileNode node; ChangeInfo info; ChangeNode changeNumberNode; String file, cmd, change, type; boolean locked; _root.removeAllChildren(); while( (message = formatter.getNextMessage()) != null) { if ( !message.startsWith(SLASH_SLASH)) { continue; } message = message.trim(); idx = message.indexOf(" - "); if ( idx == -1 ) { continue; } // format of message //hecjavadev/code/hec/client/ClientWorkspace.java#1 - edit change 171 (text) *locked* file = message.substring(0, idx); message = message.substring(idx+3); idx = message.indexOf(" "); cmd = message.substring(0, idx); message = message.substring(idx); idx = message.indexOf("("); change = message.substring(0, idx).trim(); message = message.substring(idx); idx = message.indexOf(")"); type = message.substring(1,idx); locked = message.endsWith("*locked*"); node = new ChangeFileNode(new ChangeInfo(file, cmd, type, locked)); changeNumberNode = findChangeNumberNode(change); changeNumberNode.add(node); } // now get a list of any changes that don't show with opened CommandTool.runCommand(new Command("changes -s pending"), Main.m_props); MessageWriter.outputErrorMessages(MessageFormatter.getInstance()); String client = Main.m_props.getUserName()+"@"+Main.m_props.getClientSpec(); String desc; while( (message = formatter.getNextMessage()) != null) { // format of line is // Change 208 on 2002/05/02 by mark@Q *pending* 'ResPrm ' if ( message.indexOf(client) > -1 ) { // a line for my client idx = message.indexOf(" "); idx = message.indexOf(" ", idx+1); change = message.substring(0, idx).toLowerCase(); changeNumberNode = findChangeNumberNode(change); idx = message.indexOf("'"); desc = message.substring(idx+1, message.length()-2); changeNumberNode.setDescription(desc); } } // look for an integrations that need to be performed CommandTool.runCommand(new Command("resolve -n"), Main.m_props); MessageWriter.outputErrorMessages(MessageFormatter.getInstance()); while ( (message = formatter.getNextMessage()) != null ) { // format of line is // <local file> - merging <depot file>#<num> if ( (idx = message.indexOf("merging ")) > -1 ) { idx+=8; file = message.substring(idx).trim(); idx = file.indexOf("#"); if ( idx > -1 ) { file = file.substring(0, idx); } node =findFileNode(file); if ( node != null ) { node._changeInfo.setIntegrate(true); } else { System.out.println("fillForm: failed to find file node for " +file); } } } ((DefaultTreeModel)_changeListTree.getModel()).nodeStructureChanged(_root); int rows = _changeListTree.getRowCount(); for (int i = 0; i < rows; i++ ) { _changeListTree.expandRow(i); } if ( !isVisible()) { if ( !_hasPacked ) { pack(); _hasPacked = true; } setLocationRelativeTo(getParent()); } } /** * find the node in the tree with the filename of depotFileName * @param depotFileName the name of the depot file * @return the ChangeFileNode or null if not found */ ChangeFileNode findFileNode(String depotFileName) { if ( depotFileName == null ) { return null; } Enumeration e = _root.children(); Enumeration childE; ChangeNode node; ChangeFileNode fileNode; while ( e.hasMoreElements()) { node = (ChangeNode)e.nextElement(); childE = node.children(); while (childE.hasMoreElements()) { fileNode = (ChangeFileNode)childE.nextElement(); if (fileNode.getFileNoRevision().equals(depotFileName)) { return fileNode; } } } return null; } ChangeNode findChangeNumberNode(String change) { Enumeration e = _root.children(); ChangeNode node; while ( e.hasMoreElements() ) { node = (ChangeNode)e.nextElement(); if ( change.equals(node.getUserObject())) { return node; } } node = new ChangeNode(change); if ( "default change".equals(change)) { _root.insert(node, 0); } else { _root.add(node); } return node; } public class DnDJTree extends JTree implements TreeSelectionListener, DragGestureListener, DropTargetListener, DragSourceListener { /** Stores the parent Dialog of the component */ private Frame _parent = null; /** Stores the selected node info */ protected TreePath _selectedTreePath = null; protected ChangeFileNode _selectedNode = null; /** Variables needed for DnD */ private DragSource _dragSource = null; private DragSourceContext _dragSourceContext = null; /** Constructor * @param root The root node of the tree * @param parent Parent JFrame of the JTree */ public DnDJTree(DefaultMutableTreeNode root, Frame parent) { super(root); _parent = parent; addTreeSelectionListener(DnDJTree.this); _dragSource = DragSource.getDefaultDragSource() ; DragGestureRecognizer dgr = _dragSource.createDefaultDragGestureRecognizer( this, //DragSource DnDConstants.ACTION_MOVE, //specifies valid actions this //DragGestureListener ); /* Eliminates right mouse clicks as valid actions - useful especially * if you implement a JPopupMenu for the JTree */ dgr.setSourceActions(dgr.getSourceActions() & ~InputEvent.BUTTON3_MASK); /* First argument: Component to associate the target with * Second argument: DropTargetListener */ DropTarget dropTarget = new DropTarget(this, this); //unnecessary, but gives FileManager look putClientProperty("JTree.lineStyle", "Angled"); //MetalTreeUI ui = (MetalTreeUI) getUI(); } /** Returns The selected node */ public ChangeFileNode getSelectedNode() { return _selectedNode; } ///////////////////////// Interface stuff //////////////////// /** DragGestureListener interface method */ public void dragGestureRecognized(DragGestureEvent e) { //Get the selected node ChangeFileNode dragNode = getSelectedNode(); if (dragNode != null) { //Get the Transferable Object Transferable transferable = (Transferable) dragNode.getUserObject(); if ( transferable == null ) { return; } //Select the appropriate cursor; Cursor cursor = DragSource.DefaultMoveNoDrop; int action = e.getDragAction(); if (action == DnDConstants.ACTION_MOVE) { cursor = DragSource.DefaultMoveNoDrop; } //In fact the cursor is set to NoDrop because once an action is rejected // by a dropTarget, the dragSourceListener are no more invoked. // Setting the cursor to no drop by default is so more logical, because // when the drop is accepted by a component, then the cursor is changed by the // dropActionChanged of the default DragSource. //begin the drag _dragSource.startDrag(e, cursor, transferable, this); } } /** DragSourceListener interface method */ public void dragDropEnd(DragSourceDropEvent dsde) { } /** DragSourceListener interface method */ public void dragEnter(DragSourceDragEvent dsde) { DragSourceContext context = dsde.getDragSourceContext(); //intersection of the users selected action, and the source and target actions int myaction = dsde.getDropAction(); if( (myaction & DnDConstants.ACTION_MOVE) != 0) { context.setCursor(DragSource.DefaultCopyDrop); } else { context.setCursor(DragSource.DefaultCopyNoDrop); } } /** DragSourceListener interface method */ public void dragOver(DragSourceDragEvent dsde) { DragSourceContext context = dsde.getDragSourceContext(); //intersection of the users selected action, and the source and target actions int myaction = dsde.getDropAction(); if( (myaction & DnDConstants.ACTION_MOVE) != 0) { context.setCursor(DragSource.DefaultCopyDrop); } else { context.setCursor(DragSource.DefaultCopyNoDrop); } } /** DragSourceListener interface method */ public void dropActionChanged(DragSourceDragEvent dsde) { } /** DragSourceListener interface method */ public void dragExit(DragSourceEvent dsde) { DragSourceContext context = dsde.getDragSourceContext(); context.setCursor(DragSource.DefaultCopyNoDrop); } /** DropTargetListener interface method - What we do when drag is released */ public void drop(DropTargetDropEvent e) { try { Transferable tr = e.getTransferable(); //flavor not supported, reject drop if (!tr.isDataFlavorSupported( ChangeInfo.INFO_FLAVOR)) { e.rejectDrop(); } //cast into appropriate data type ChangeInfo childInfo = (ChangeInfo) tr.getTransferData( ChangeInfo.INFO_FLAVOR ); //get new parent node Point loc = e.getLocation(); TreePath destinationPath = getPathForLocation(loc.x, loc.y); final String msg = testDropTarget(destinationPath, _selectedTreePath); if (msg != null) { e.rejectDrop(); SwingUtilities.invokeLater(new Runnable() { public void run() { JOptionPane.showMessageDialog( DnDJTree.this, msg, "Error Dialog", JOptionPane.ERROR_MESSAGE ); } }); return; } // reopen the node DefaultMutableTreeNode node = (DefaultMutableTreeNode)destinationPath.getLastPathComponent(); if ( !(node instanceof ChangeNode )) { e.rejectDrop(); return; } ChangeNode cNode = (ChangeNode)node; ChangeFileNode cfNode =(ChangeFileNode)_selectedTreePath.getLastPathComponent(); String file = cfNode.getFile(); int idx = file.indexOf("#"); if ( idx > -1 ) { // strip off "#2" file = cfNode.getFile().substring(0,idx); } String cmd = "reopen -c "+cNode.getChangeNumber()+ " " + file; CommandTool.runCommand(new Command(cmd), Main.m_props); // if an error occured reject drop if ( MessageFormatter.getErrorMessageCount() != 0 ) { MessageWriter.outputErrorMessages(MessageFormatter.getInstance()); e.rejectDrop(); return; } DefaultMutableTreeNode newParent = (DefaultMutableTreeNode) destinationPath.getLastPathComponent(); //get old parent node DefaultMutableTreeNode oldParent = (DefaultMutableTreeNode)getSelectedNode().getParent(); int action = e.getDropAction(); //boolean copyAction = (action == DnDConstants.ACTION_COPY); //make new child node ChangeFileNode newChild = new ChangeFileNode(childInfo); try { /*if (!copyAction)*/ oldParent.remove(getSelectedNode()); newParent.add(newChild); //if (copyAction) e.acceptDrop (DnDConstants.ACTION_COPY); //else e.acceptDrop (DnDConstants.ACTION_MOVE); } catch (java.lang.IllegalStateException ils) { e.rejectDrop(); } e.getDropTargetContext().dropComplete(true); //expand nodes appropriately - this probably isnt the best way... DefaultTreeModel model = (DefaultTreeModel) getModel(); model.reload(oldParent); model.reload(newParent); TreePath parentPath = new TreePath(newParent.getPath()); expandPath(parentPath); } catch (IOException io) { e.rejectDrop(); } catch (UnsupportedFlavorException ufe) {e.rejectDrop();} } /** DropTaregetListener interface method */ public void dragEnter(DropTargetDragEvent e) { } /** DropTaregetListener interface method */ public void dragExit(DropTargetEvent e) { } /** DropTargetListener interface method */ public void dragOver(DropTargetDragEvent e) { //set cursor location. Needed in setCursor method Point loc = e.getLocation(); TreePath destinationPath = getPathForLocation(loc.x, loc.y); // if destination path is okay accept drop... String msg = testDropTarget(destinationPath, _selectedTreePath); if (msg == null) { e.acceptDrag(DnDConstants.ACTION_MOVE ) ; } // ...otherwise reject drop else { e.rejectDrag(); } } /** DropTaregetListener interface method */ public void dropActionChanged(DropTargetDragEvent e) { } /** TreeSelectionListener - sets selected node */ public void valueChanged(TreeSelectionEvent evt) { _selectedTreePath = evt.getNewLeadSelectionPath(); if (_selectedTreePath == null) { _selectedNode = null; return; } if ( _selectedTreePath.getLastPathComponent() instanceof ChangeFileNode ) { _selectedNode = (ChangeFileNode)_selectedTreePath.getLastPathComponent(); } } /** Convenience method to test whether drop location is valid * @param destination The destination path * @param dropper The path for the node to be dropped * @return null if no problems, otherwise an explanation */ private String testDropTarget(TreePath destination, TreePath dropper) { //Typical Tests for dropping //Test 1. boolean destinationPathIsNull = destination == null; if (destinationPathIsNull) { return "Invalid drop location."; } //Test 2. DefaultMutableTreeNode node = (DefaultMutableTreeNode) destination.getLastPathComponent(); if ( !node.getAllowsChildren() ) { return "This node does not allow children"; } if ( destination.getLastPathComponent() == _root ) { return "Can't drop on root"; } if (destination.equals(dropper)) { return "Destination cannot be same as source"; } //Test 3. if ( dropper.isDescendant(destination)) { return "Destination node cannot be a descendant."; } //Test 4. if ( dropper.getParentPath().equals(destination)) { return "Destination node cannot be a parent."; } return null; } } //end of DnDJTree static class ChangeInfo implements Transferable, java.io.Serializable { public final static DataFlavor INFO_FLAVOR = new DataFlavor(ChangeInfo.class, "Change Information"); static DataFlavor flavors[] = {INFO_FLAVOR }; private String _file; private String _type; private String _cmd; private boolean _locked; private String _toString; private boolean _integrate; public ChangeInfo (String fileName, String cmd, String type, boolean locked) { _file = fileName; _type = type; _cmd = cmd; _locked = locked; } public String toString() { if ( _toString == null ) { _toString = _file + " <"+_type+"><"+_cmd+">"; } return _toString; } public boolean isLocked() { return _locked; } public boolean isAdd() { return "add".equalsIgnoreCase(_cmd); } public boolean isEdit() { return "edit".equalsIgnoreCase(_cmd); } public boolean isDelete() { return "delete".equalsIgnoreCase(_cmd); } public boolean getIntegrate() { return _integrate; } public void setIntegrate(boolean integrate) { _integrate = integrate; } public String getFile() { return _file; } // --------- Transferable -------------- public boolean isDataFlavorSupported(DataFlavor df) { return df.equals(INFO_FLAVOR); } /** implements Transferable interface */ public Object getTransferData(DataFlavor df) throws UnsupportedFlavorException, IOException { if (df.equals(INFO_FLAVOR)) { return this; } else throw new UnsupportedFlavorException(df); } /** implements Transferable interface */ public DataFlavor[] getTransferDataFlavors() { return flavors; } } }
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#3 | 4090 | David Freels |
Fixed bug where paths with spaces would not work. Also checked in new code or JBuilderX. |
||
#2 | 2994 | Mark E. Ackerman |
changed for version 1.0.7. Added display of submitted changelists, redo sync, updated icons, misc bug fixes and improvements. |
||
#1 | 1873 | Mark E. Ackerman | Version 1.0 build. |