hu.pagavcs.client.gui.repobrowser.RepoBrowserGui.java Source code

Java tutorial

Introduction

Here is the source code for hu.pagavcs.client.gui.repobrowser.RepoBrowserGui.java

Source

package hu.pagavcs.client.gui.repobrowser;

import hu.pagavcs.client.bl.Cancelable;
import hu.pagavcs.client.bl.Manager;
import hu.pagavcs.client.bl.OnSwing;
import hu.pagavcs.client.bl.OnSwingWait;
import hu.pagavcs.client.bl.PagaException;
import hu.pagavcs.client.bl.ThreadAction;
import hu.pagavcs.client.gui.LocationCallback;
import hu.pagavcs.client.gui.Working;
import hu.pagavcs.client.gui.action.ShowLogAction;
import hu.pagavcs.client.gui.platform.EditField;
import hu.pagavcs.client.gui.platform.Frame;
import hu.pagavcs.client.gui.platform.GuiHelper;
import hu.pagavcs.client.gui.platform.Label;
import hu.pagavcs.client.gui.platform.MessagePane;
import hu.pagavcs.client.gui.platform.MessagePane.OPTIONS;
import hu.pagavcs.client.gui.platform.Tree;
import hu.pagavcs.client.operation.Checkout;
import hu.pagavcs.client.operation.RepoBrowser;
import hu.pagavcs.client.operation.RepoBrowser.RepoBrowserStatus;
import hu.pagavcs.common.ResourceBundleAccessor;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.KeyStroke;
import javax.swing.SwingConstants;
import javax.swing.event.TreeExpansionEvent;
import javax.swing.event.TreeWillExpandListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.ExpandVetoException;
import javax.swing.tree.TreePath;

import org.tmatesoft.svn.core.SVNDirEntry;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNNodeKind;
import org.tmatesoft.svn.core.SVNURL;

import com.jgoodies.forms.layout.CellConstraints;
import com.jgoodies.forms.layout.FormLayout;

/**
 * PagaVCS 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 2 of the License, or (at your option) any later
 * version.<br>
 * <br>
 * PagaVCS 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.<br>
 * <br>
 * You should have received a copy of the GNU General Public License along with
 * PagaVCS; If not, see http://www.gnu.org/licenses/.
 */
public class RepoBrowserGui implements Working, Cancelable, TreeWillExpandListener {

    private RepoBrowser repoBrowser;
    private Label lblWorkingCopy;
    private EditField sfUrl;
    private Label lblStatus;
    private JTree tree;
    private String rootUrl;
    private Frame frame;
    private Label lblTreeWorking;

    public RepoBrowserGui(RepoBrowser repoBrowser) {
        this.repoBrowser = repoBrowser;
    }

    public void display() throws SVNException {
        FormLayout layout = new FormLayout("right:p, 2dlu,p:g,p", "p,2dlu,p,2dlu,p:g,2dlu,p");
        JPanel pnlMain = new JPanel(layout);
        CellConstraints cc = new CellConstraints();
        lblWorkingCopy = new Label();
        sfUrl = new EditField();
        sfUrl.addFocusListener(new FocusListener() {

            public void focusLost(FocusEvent e) {
                try {
                    urlChanged();
                } catch (Exception ex) {
                    Manager.handle(ex);
                }
            }

            public void focusGained(FocusEvent e) {
            }
        });
        sfUrl.addKeyListener(new KeyAdapter() {

            public void keyTyped(KeyEvent e) {
                if (e.getKeyChar() == '\n') {
                    try {
                        urlChanged();
                    } catch (Exception ex) {
                        Manager.handle(ex);
                    }
                }
            }
        });

        JButton btnRefresh = new JButton(new RefreshNodeAction());

        lblStatus = new Label();
        tree = new Tree();
        tree.addTreeWillExpandListener(this);
        tree.addMouseListener(new PopupupMouseListener());

        tree.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("F5"), "REFRESH_SVN_TREE");
        tree.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("control R"),
                "REFRESH_SVN_TREE");
        tree.getActionMap().put("REFRESH_SVN_TREE", new RefreshNodeAction());

        tree.setCellRenderer(new RepoTreeCellRender());
        treeWorking();

        pnlMain.add(new JLabel("URL:"), cc.xywh(1, 1, 1, 1));
        pnlMain.add(sfUrl, cc.xywh(3, 1, 1, 1));
        pnlMain.add(btnRefresh, cc.xywh(4, 1, 1, 1));

        pnlMain.add(new JLabel("Working copy:"), cc.xywh(1, 3, 1, 1));
        pnlMain.add(lblWorkingCopy, cc.xywh(3, 3, 1, 1));

        pnlMain.add(new JScrollPane(tree), cc.xywh(1, 5, 4, 1, CellConstraints.FILL, CellConstraints.FILL));

        pnlMain.add(lblStatus, cc.xywh(4, 7, 1, 1));

        frame = GuiHelper.createAndShowFrame(pnlMain, "Repository Browser");
    }

    private void treeWorking() {
        lblTreeWorking = new Label("Working...");
        lblTreeWorking.setIcon(Manager.getIconInformation());
        lblTreeWorking.setHorizontalAlignment(SwingConstants.CENTER);
        lblTreeWorking.setVerticalAlignment(SwingConstants.CENTER);

        tree.setLayout(new BorderLayout());
        tree.add(lblTreeWorking, BorderLayout.CENTER);
        tree.revalidate();
        tree.repaint();
    }

    private void treeFinished() {
        if (lblTreeWorking != null) {
            tree.remove(lblTreeWorking);
            lblTreeWorking = null;
        }
    }

    private RepoTreeNode getSelectedRepoTreeNode() {
        TreePath path = tree.getSelectionPath();

        if (path == null) {
            return null;
        }

        DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
        return (RepoTreeNode) node.getUserObject();
    }

    private class UrlChangedAction extends ThreadAction {

        public UrlChangedAction() {
            super("URL changed");
        }

        public void actionProcess(ActionEvent e) throws Exception {
            if ((rootUrl == null || !rootUrl.equals(sfUrl.getText())) && !sfUrl.getText().trim().isEmpty()) {
                new OnSwing() {

                    protected void process() throws Exception {
                        workStarted();
                    }
                }.run();
                rootUrl = sfUrl.getText();

                final List<SVNDirEntry> lstDirChain = repoBrowser.getDirEntryChain(rootUrl);
                final DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode("Root Node");
                new OnSwingWait<Object, Object>(null) {

                    protected Object process() throws Exception {
                        tree.setRootVisible(false);
                        tree.setModel(new DefaultTreeModel(rootNode));
                        tree.requestFocus();

                        return null;
                    }
                }.run();

                new OnSwing() {

                    protected void process() throws Exception {
                        treeFinished();
                        addRootNode(rootNode, rootUrl, lstDirChain);
                        workEnded();
                    }
                }.run();
            }
        }
    }

    public void urlChanged() throws Exception {
        new UrlChangedAction().actionPerformed(null);
    }

    public void setStatus(RepoBrowserStatus status) {
        lblStatus.setText("Status: " + status.toString());
    }

    public void workStarted() {
        setStatus(RepoBrowserStatus.WORKING);
    }

    public void workEnded() {
        setStatus(RepoBrowserStatus.COMPLETED);
    }

    public boolean isCancel() {
        return repoBrowser.isCancel();
    }

    public void setCancel(boolean cancel) throws Exception {
        repoBrowser.setCancel(true);
    }

    public void setURL(final String url) throws Exception {
        sfUrl.setText(url);
        urlChanged();
    }

    private DefaultMutableTreeNode addNode(DefaultMutableTreeNode parentNode, SVNDirEntry dirEntry) {
        RepoTreeNode treeNode = new RepoTreeNode(dirEntry);
        DefaultMutableTreeNode node = new DefaultMutableTreeNode(treeNode);
        parentNode.add(node);

        if (dirEntry.getKind().equals(SVNNodeKind.DIR)) {
            treeNode.setLoaded(false);
            node.add(new DefaultMutableTreeNode("..."));
        } else {
            treeNode.setLoaded(true);
        }

        return node;
    }

    private void addRootNode(DefaultMutableTreeNode parentNode, String url, List<SVNDirEntry> lstDirChain)
            throws Exception {

        final DefaultMutableTreeNode realParentNode = parentNode;
        final ArrayList<DefaultMutableTreeNode> lstPath = new ArrayList<DefaultMutableTreeNode>();
        lstPath.add(parentNode);

        for (SVNDirEntry li : lstDirChain) {
            parentNode = addNode(parentNode, li);
            lstPath.add(parentNode);
        }

        new OnSwing(true) {

            protected void process() throws Exception {
                ArrayList<DefaultMutableTreeNode> lstNewPath = new ArrayList<DefaultMutableTreeNode>();
                DefaultMutableTreeNode pNode = realParentNode;

                lstNewPath.add(pNode);
                tree.expandPath(new TreePath(lstNewPath.toArray()));

                for (int i = 1; i < lstPath.size(); i++) {

                    DefaultMutableTreeNode path = lstPath.get(i);

                    for (int c = 0; c < pNode.getChildCount(); c++) {
                        DefaultMutableTreeNode child = (DefaultMutableTreeNode) pNode.getChildAt(c);
                        RepoTreeNode userObject = (RepoTreeNode) child.getUserObject();
                        if (path.getUserObject().equals(userObject)) {
                            lstNewPath.add(child);
                            pNode = child;
                            break;
                        }
                    }
                    tree.expandPath(new TreePath(lstNewPath.toArray()));
                }
                TreePath lastTreePath = new TreePath(lstNewPath.toArray());
                tree.makeVisible(lastTreePath);
                tree.scrollPathToVisible(lastTreePath);
                tree.setSelectionPath(lastTreePath);
            }
        }.run();

    }

    private void loadChildren(DefaultMutableTreeNode parentNode, SVNDirEntry url)
            throws SVNException, PagaException {

        if (url.getKind().equals(SVNNodeKind.DIR)) {
            List<SVNDirEntry> lstChildren = repoBrowser.getChilds(url.getURL().toDecodedString());
            Collections.sort(lstChildren, SvnDirEntryComparator.getInstance());
            for (SVNDirEntry child : lstChildren) {
                addNode(parentNode, child);
            }
        }
    }

    public void setWorkingCopy(final String workingCopy) throws Exception {
        new OnSwing() {

            protected void process() throws Exception {
                lblWorkingCopy.setText(workingCopy);
            }
        }.run();
    }

    public void treeWillCollapse(TreeExpansionEvent event) throws ExpandVetoException {
    }

    public void treeWillExpand(TreeExpansionEvent event) throws ExpandVetoException {
        try {
            DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode) event.getPath().getLastPathComponent();
            if (!(parentNode.getUserObject() instanceof RepoTreeNode)) {
                return;
            }

            RepoTreeNode parentTreeNode = (RepoTreeNode) parentNode.getUserObject();

            if (!parentTreeNode.isLoaded()) {
                parentNode.removeAllChildren();
                loadChildren(parentNode, parentTreeNode.getSvnDirEntry());
                parentTreeNode.setLoaded(true);
            }
        } catch (Exception e) {
            Manager.handle(e);
        }
    }

    private static class RepoTreeNode {

        private final SVNDirEntry svnDirEntry;
        private boolean loaded;

        public RepoTreeNode(SVNDirEntry svnDirEntry) {
            this.svnDirEntry = svnDirEntry;
        }

        public String toString() {
            if (loaded) {
                return svnDirEntry.getName();
            }
            return svnDirEntry.getName();
        }

        public SVNDirEntry getSvnDirEntry() {
            return svnDirEntry;
        }

        public void setLoaded(boolean loaded) {
            this.loaded = loaded;
        }

        public boolean isLoaded() {
            return loaded;
        }

        public boolean equals(Object obj) {
            if (!(obj instanceof RepoTreeNode)) {
                return false;
            }
            return ((RepoTreeNode) obj).getSvnDirEntry().getRelativePath().equals(svnDirEntry.getRelativePath());
        }

        public int hashCode() {
            return svnDirEntry.hashCode();
        }
    }

    private static class SvnDirEntryComparator implements Comparator<SVNDirEntry> {

        private static SvnDirEntryComparator singleton;

        public static SvnDirEntryComparator getInstance() {
            if (singleton == null) {
                singleton = new SvnDirEntryComparator();
            }
            return singleton;
        }

        public int compare(SVNDirEntry o1, SVNDirEntry o2) {
            if (o1.getKind().equals(SVNNodeKind.DIR) && !o2.getKind().equals(SVNNodeKind.DIR)) {
                return -1;
            }
            if (!o1.getKind().equals(SVNNodeKind.DIR) && o2.getKind().equals(SVNNodeKind.DIR)) {
                return 1;
            }

            return o1.getName().compareTo(o2.getName());
        }

    }

    private class RefreshNodeAction extends ThreadAction {

        public RefreshNodeAction() {
            super("Refresh", ResourceBundleAccessor.getSmallImage("actions/pagavcs-refresh.png"));
        }

        public void actionProcess(ActionEvent e) throws Exception {
            new OnSwing() {

                protected void process() throws Exception {
                    TreePath path = tree.getSelectionPath();
                    RepoTreeNode li = getSelectedRepoTreeNode();
                    if (li != null) {
                        li.setLoaded(false);
                        tree.collapsePath(path);
                        tree.expandPath(path);

                        ((DefaultTreeModel) tree.getModel())
                                .nodeStructureChanged((javax.swing.tree.TreeNode) path.getLastPathComponent());
                    }
                }
            }.run();

        }
    }

    private class CreateFolderAction extends ThreadAction {

        private final PopupupMouseListener popupupMouseListener;
        private boolean doCreate;
        private String folderName;
        private String logMessage;

        public CreateFolderAction(PopupupMouseListener popupupMouseListener) {
            super("Create folder", ResourceBundleAccessor.getSmallImage("actions/pagavcs-add.png"));
            this.popupupMouseListener = popupupMouseListener;
        }

        public void actionProcess(ActionEvent e) throws Exception {
            doCreate = false;
            new OnSwingWait<Object, Object>() {

                protected Object process() throws Exception {
                    CreateFolderDialog dialog = new CreateFolderDialog();
                    dialog.display(frame);
                    if (dialog.isDoCreate()) {
                        doCreate = true;
                        folderName = dialog.getFolderName();
                        logMessage = dialog.getLogMessage();
                    }
                    return null;
                }
            }.run();

            if (doCreate) {
                RepoTreeNode li = popupupMouseListener.getSelected();
                repoBrowser.createFolder(li.getSvnDirEntry(), folderName, logMessage);
                new RefreshNodeAction().actionProcess(null);
            }
        }
    }

    private class CheckoutAction extends ThreadAction {

        private final PopupupMouseListener popupupMouseListener;

        public CheckoutAction(PopupupMouseListener popupupMouseListener) {
            super("Checkout", ResourceBundleAccessor.getSmallImage("actions/pagavcs-checkout.png"));
            this.popupupMouseListener = popupupMouseListener;
        }

        public void actionProcess(ActionEvent e) throws Exception {
            RepoTreeNode li = popupupMouseListener.getSelected();

            Checkout checkout = new Checkout(repoBrowser.getPath(), li.getSvnDirEntry().getURL().toDecodedString());
            checkout.execute();
        }
    }

    private class CopyUrlToClipboard extends AbstractAction {

        private final PopupupMouseListener popupupMouseListener;

        public CopyUrlToClipboard(PopupupMouseListener popupupMouseListener) {
            super("Copy URL to Clipboard", ResourceBundleAccessor.getSmallImage("actions/pagavcs-dbus.png"));
            this.popupupMouseListener = popupupMouseListener;
        }

        public void actionPerformed(ActionEvent e) {
            RepoTreeNode li = popupupMouseListener.getSelected();
            Manager.setClipboard(li.getSvnDirEntry().getURL().toDecodedString());
        }
    }

    private class DeleteAction extends AbstractAction {

        private final PopupupMouseListener popupupMouseListener;

        public DeleteAction(PopupupMouseListener popupupMouseListener) {
            super("Delete", ResourceBundleAccessor.getSmallImage("actions/pagavcs-delete.png"));
            this.popupupMouseListener = popupupMouseListener;
        }

        public void actionPerformed(ActionEvent e) {
            RepoTreeNode li = popupupMouseListener.getSelected();
            final SVNURL svnUrl = li.getSvnDirEntry().getURL();
            String url = svnUrl.toDecodedString();

            OPTIONS answer = MessagePane.executeConfirmDialog(frame,
                    "Are you sure you want to delete <" + url + ">?");
            if (answer == OPTIONS.OK) {
                final String message = MessagePane.executeInputDialog(frame, "Delete message",
                        "Commit message for the deletion?");
                if (message == null || message.trim().isEmpty()) {
                    MessagePane.showInfo(frame, "Cancelled", "Deletion is cancelled.");
                } else {
                    new Thread(new Runnable() {
                        public void run() {
                            try {
                                repoBrowser.delete(svnUrl, message);
                                rootUrl = null;
                                new UrlChangedAction().actionProcess(null);
                            } catch (Exception ex) {
                                Manager.handle(ex);
                            }
                        }
                    }).start();
                }
            }
        }
    }

    private class CopyAction extends ThreadAction {

        private final PopupupMouseListener popupupMouseListener;

        public CopyAction(PopupupMouseListener popupupMouseListener) {
            super("Copy", ResourceBundleAccessor.getSmallImage("actions/pagavcs-copy.png"));
            this.popupupMouseListener = popupupMouseListener;
        }

        public void actionProcess(ActionEvent e) throws Exception {
            RepoTreeNode li = popupupMouseListener.getSelected();

            repoBrowser.copy(li.getSvnDirEntry().getURL().toDecodedString());

        }
    }

    private class PopupupMouseListener extends MouseAdapter {

        private JPopupMenu ppAll;
        private RepoTreeNode selected;
        private TreePath path;

        public PopupupMouseListener() {
            ppAll = new JPopupMenu();
            ppAll.add(new RefreshNodeAction());
            ppAll.add(new CopyUrlToClipboard(this));
            ppAll.add(new CheckoutAction(this));
            ppAll.add(new ShowLogAction(new LocationCallback() {

                @Override
                public boolean isFilePath() {
                    return false;
                }

                @Override
                public String getLocation() {
                    return getSelectedRepoTreeNode().getSvnDirEntry().getURL().toString();
                }
            }));
            ppAll.add(new CreateFolderAction(this));
            ppAll.add(new DeleteAction(this));
        }

        public RepoTreeNode getSelected() {
            return selected;
        }

        private void showPopup(MouseEvent e) {

            int x = e.getX();
            int y = e.getY();
            JTree tree = (JTree) e.getSource();
            path = tree.getPathForLocation(x, y);
            if (path == null) {
                return;
            }

            tree.setSelectionPath(path);

            DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
            selected = (RepoTreeNode) node.getUserObject();
            JPopupMenu ppVisible = ppAll;
            ppVisible.setInvoker(tree);
            ppVisible.setLocation(e.getXOnScreen(), e.getYOnScreen());
            ppVisible.setVisible(true);
            e.consume();
        }

        public void mousePressed(MouseEvent e) {
            if (e.isPopupTrigger())
                showPopup(e);
        }

        public void mouseReleased(MouseEvent e) {
            if (e.isPopupTrigger())
                showPopup(e);
        }
    }

    private static class RepoTreeCellRender extends DefaultTreeCellRenderer {

        private static final Color COLOR_NON_EXPANDED = new Color(27, 0, 116);
        private Font italicFont;
        private Font normalFont;

        @Override
        public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded,
                boolean leaf, int row, boolean hasFocus) {
            Component ret = super.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row,
                    hasFocus);

            JLabel label = (JLabel) ret;

            // Object userObj = ((DefaultMutableTreeNode)
            // value).getUserObject();

            if (!selected && !expanded && !leaf) {
                label.setForeground(COLOR_NON_EXPANDED);
            }

            if (!expanded && !leaf) {
                if (italicFont == null) {
                    normalFont = label.getFont();
                    italicFont = normalFont.deriveFont(Font.ITALIC);
                }
                label.setFont(italicFont);
            } else {
                if (normalFont != null) {
                    label.setFont(normalFont);
                }
            }

            return ret;
        }

    }
}