com.vectrace.MercurialEclipse.views.PatchQueueView.java Source code

Java tutorial

Introduction

Here is the source code for com.vectrace.MercurialEclipse.views.PatchQueueView.java

Source

/*******************************************************************************
 * Copyright (c) 2005-2010 VecTrace (Zingo Andersen) and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 * bastian   implementation
 *     Andrei Loskutov           - bug fixes
 *     Philip Graf               - bug fix, popup menu
 *******************************************************************************/
package com.vectrace.MercurialEclipse.views;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.GroupMarker;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.PartInitException;

import com.aragost.javahg.ext.mq.Patch;
import com.vectrace.MercurialEclipse.MercurialEclipsePlugin;
import com.vectrace.MercurialEclipse.SafeUiJob;
import com.vectrace.MercurialEclipse.commands.extensions.mq.HgQFinishClient;
import com.vectrace.MercurialEclipse.commands.extensions.mq.HgQFoldClient;
import com.vectrace.MercurialEclipse.commands.extensions.mq.HgQPopClient;
import com.vectrace.MercurialEclipse.commands.extensions.mq.HgQPushClient;
import com.vectrace.MercurialEclipse.commands.extensions.mq.HgQSeriesClient;
import com.vectrace.MercurialEclipse.dialogs.RejectsDialog;
import com.vectrace.MercurialEclipse.exception.HgException;
import com.vectrace.MercurialEclipse.menu.QDeleteHandler;
import com.vectrace.MercurialEclipse.menu.QImportHandler;
import com.vectrace.MercurialEclipse.menu.QNewHandler;
import com.vectrace.MercurialEclipse.menu.QRefreshHandler;
import com.vectrace.MercurialEclipse.menu.StripHandler;
import com.vectrace.MercurialEclipse.preferences.MercurialPreferenceConstants;
import com.vectrace.MercurialEclipse.team.MercurialTeamProvider;
import com.vectrace.MercurialEclipse.team.cache.RefreshRootJob;
import com.vectrace.MercurialEclipse.team.cache.RefreshWorkspaceStatusJob;
import com.vectrace.MercurialEclipse.ui.PatchTable;
import com.vectrace.MercurialEclipse.ui.TableSortListener;

/**
 * @author bastian
 *
 */
public class PatchQueueView extends AbstractRootView {

    public static final String ID = PatchQueueView.class.getName();

    private PatchTable table;

    private Action qNewAction;
    private Action qRefreshAction;
    private Action qPushAllAction;
    private Action qPopAllAction;
    private Action qDeleteAction;
    private Action qFinishAction;
    private Action qFoldAction;
    private Action qImportAction;
    private Action qGotoAction;
    private Action stripAction;
    private Patch topmostAppliedPatch;

    /**
     * @see com.vectrace.MercurialEclipse.views.AbstractRootView#createTable(org.eclipse.swt.widgets.Composite)
     */
    @Override
    protected void createTable(Composite parent) {
        table = new PatchTable(parent);
        getSite().setSelectionProvider(table.getTableViewer());
    }

    /**
     * @see com.vectrace.MercurialEclipse.views.AbstractRootView#createToolBar(org.eclipse.jface.action.IToolBarManager)
     */
    @Override
    protected void createToolBar(IToolBarManager mgr) {
        mgr.add(makeActionContribution(qImportAction));
        mgr.add(makeActionContribution(qNewAction));
        mgr.add(makeActionContribution(qRefreshAction));
        mgr.add(makeActionContribution(qFinishAction));
        mgr.add(makeActionContribution(qFoldAction));
        mgr.add(makeActionContribution(qDeleteAction));
        mgr.add(makeActionContribution(stripAction));
    }

    /**
     * @see com.vectrace.MercurialEclipse.views.AbstractRootView#createActions()
     */
    @Override
    protected void createActions() {
        qImportAction = new Action("qimport...", MercurialEclipsePlugin.getImageDescriptor("import.gif")) { //$NON-NLS-1$
            @Override
            public void run() {
                try {
                    QImportHandler.openWizard(hgRoot, getSite().getShell());
                } catch (Exception e) {
                    MercurialEclipsePlugin.logError(e);
                }
            }
        };
        qImportAction.setEnabled(false);

        qNewAction = new Action("qnew...", MercurialEclipsePlugin.getImageDescriptor("import.gif")) { //$NON-NLS-1$
            @Override
            public void run() {
                try {
                    QNewHandler.openWizard(hgRoot, getSite().getShell());
                } catch (Exception e) {
                    MercurialEclipsePlugin.logError(e);
                }
            }
        };
        qNewAction.setEnabled(false);

        qRefreshAction = new Action("qrefresh...", //$NON-NLS-1$
                MercurialEclipsePlugin.getImageDescriptor("actions/qrefresh.gif")) {
            @Override
            public void run() {
                QRefreshHandler.openWizard(hgRoot, getSite().getShell());
            }
        };
        qRefreshAction.setEnabled(false);

        qGotoAction = new MQPatchAction(Messages.getString("PatchQueueView.switchTo"),
                RefreshRootJob.LOCAL_AND_OUTGOING, "QPushRejectsDialog.conflict", false) {
            @Override
            public boolean doInvoke() throws HgException {
                // Switch to the first selected patch. There is only one patch selected because
                // the action is disabled if zero or more than one patches are selected.
                Patch patch = table.getSelection();
                if (patch != null) {
                    if (patch.isApplied()) {
                        HgQPopClient.pop(hgRoot, false, patch.getName());
                    } else {
                        HgQPushClient.push(hgRoot, false, patch.getName());
                    }
                    return true;
                }
                return false;
            }
        };
        qGotoAction.setImageDescriptor(MercurialEclipsePlugin.getImageDescriptor("actions/switch.gif"));
        qGotoAction.setEnabled(false);

        qPushAllAction = new MQPatchAction(Messages.getString("PatchQueueView.applyAll"),
                RefreshRootJob.LOCAL_AND_OUTGOING, "QPushRejectsDialog.conflict", true) {
            @Override
            public boolean doInvoke() throws HgException {
                HgQPushClient.pushAll(hgRoot, false);
                return true;
            }
        };
        qPushAllAction.setEnabled(true);

        qPopAllAction = new MQAction(Messages.getString("PatchQueueView.unapplyAll"),
                RefreshRootJob.LOCAL_AND_OUTGOING) {
            @Override
            public boolean invoke() throws HgException {
                HgQPopClient.popAll(hgRoot, false);
                return true;
            }
        };
        qPopAllAction.setEnabled(false);

        qFoldAction = new MQPatchAction("qfold", RefreshRootJob.LOCAL_AND_OUTGOING, "QFoldRejectsDialog.conflict",
                false) {
            @Override
            public boolean doInvoke() throws HgException {
                List<Patch> patches = new ArrayList<Patch>(table.getSelections());
                if (patches.size() > 0) {
                    Collections.sort(patches, new Comparator<Patch>() {
                        public int compare(Patch o1, Patch o2) {
                            return TableSortListener.sort(o1.getIndex(), o2.getIndex());
                        }
                    });
                    HgQFoldClient.fold(hgRoot, true, null, patches);
                    return true;
                }
                return false;
            }

            @Override
            protected boolean isPatchConflict(HgException e) {
                return HgQFoldClient.isPatchConflict(e);
            }
        };
        qFoldAction.setEnabled(false);

        stripAction = new Action("strip...", MercurialEclipsePlugin.getImageDescriptor("actions/revert.gif")) { //$NON-NLS-1$
            @Override
            public void run() {
                try {
                    // TODO: set initial selection to selected applied patch
                    StripHandler.openWizard(hgRoot, getSite().getShell(), null);
                } catch (Exception e) {
                    MercurialEclipsePlugin.logError(e);
                }
            }
        };
        stripAction.setEnabled(false);

        qDeleteAction = new Action("qdelete...", MercurialEclipsePlugin.getImageDescriptor("rem_co.gif")) { //$NON-NLS-1$
            @Override
            public void run() {
                QDeleteHandler.openWizard(hgRoot, getSite().getShell(), false);
            }
        };
        qDeleteAction.setEnabled(false);

        qFinishAction = new MQAction("qfinish", MercurialEclipsePlugin.getImageDescriptor("actions/qrefresh.gif"),
                RefreshRootJob.LOCAL) {
            @Override
            public boolean invoke() throws HgException {
                List<Patch> patches = table.getSelections();
                Patch max = null;
                boolean unappliedSelected = false;

                for (Patch p : patches) {
                    if (p.isApplied()) {
                        if (max == null || p.getIndex() > max.getIndex()) {
                            max = p;
                        }
                    } else {
                        unappliedSelected = true;
                    }
                }

                List<Patch> toApply = new ArrayList<Patch>(table.getItems());

                for (Iterator<Patch> it = toApply.iterator(); it.hasNext();) {
                    Patch cur = it.next();

                    if (!cur.isApplied()) {
                        it.remove();
                    } else if (max != null && cur.getIndex() > max.getIndex()) {
                        it.remove();
                    }
                }

                if (toApply.size() == 0) {
                    MessageDialog.openInformation(getSite().getShell(), "No applied patches",
                            "Only applied patches can be promoted.");
                } else if (max == null) {
                    if (unappliedSelected) {
                        MessageDialog.openInformation(getSite().getShell(), "Only applied patches can be promoted",
                                "Only applied patches can be promoted. Select an applied patch to promote.");
                    } else if (MercurialEclipsePlugin.showDontShowAgainConfirmDialog("Promote all applied patches?",
                            "No patches selected, promote all " + toApply.size() + " applied patches?",
                            MessageDialog.CONFIRM, MercurialPreferenceConstants.PREF_SHOW_QFINISH_WARNING_DIALOG,
                            getSite().getShell())) {
                        HgQFinishClient.finish(hgRoot, toApply);
                        return true;
                    }
                } else if (toApply.size() == 1 || MercurialEclipsePlugin.showDontShowAgainConfirmDialog(
                        "QFinish multiple patches", "Promote " + toApply.size() + " patches?",
                        MessageDialog.CONFIRM, MercurialPreferenceConstants.PREF_SHOW_QFINISH_WARNING_DIALOG,
                        getSite().getShell())) {

                    HgQFinishClient.finish(hgRoot, toApply);
                    return true;
                }

                return false;
            }
        };
        qFinishAction.setEnabled(false);

        table.getTableViewer().addPostSelectionChangedListener(new ISelectionChangedListener() {
            public void selectionChanged(SelectionChangedEvent event) {
                updateEnablement((IStructuredSelection) event.getSelection());
            }
        });
    }

    protected void updateEnablement(IStructuredSelection selection) {
        assert hgRoot != null;

        boolean isTopSelected = false;
        boolean selectionHasApplied = false;
        for (Iterator<Patch> i = selection.iterator(); i.hasNext();) {
            Patch p = i.next();
            if (p.equals(topmostAppliedPatch)) {
                isTopSelected = true;
            }

            selectionHasApplied |= p.isApplied();
        }

        boolean isAllApplied = true;
        boolean isAllUnapplied = true;
        for (Patch patch : (List<Patch>) table.getTableViewer().getInput()) {
            if (patch.isApplied()) {
                isAllUnapplied = false;
            } else {
                isAllApplied = false;
            }
        }

        qNewAction.setEnabled(true);
        qRefreshAction.setEnabled(true);
        qImportAction.setEnabled(true);
        qDeleteAction.setEnabled(true);
        qGotoAction.setEnabled(selection.size() == 1 && !isTopSelected);
        qPushAllAction.setEnabled(!isAllApplied);
        qPopAllAction.setEnabled(!isAllUnapplied);
        qFoldAction.setEnabled(!selectionHasApplied && !selection.isEmpty());
        qFinishAction.setEnabled(selectionHasApplied || selection.isEmpty());
        stripAction.setEnabled(true);
    }

    /**
     * @see com.vectrace.MercurialEclipse.views.AbstractRootView#createMenus(org.eclipse.jface.action.IMenuManager)
     */
    @Override
    protected void createMenus(IMenuManager menuMgr) {
        menuMgr.add(qImportAction);
        menuMgr.add(qNewAction);
        menuMgr.add(qRefreshAction);
        menuMgr.add(qFinishAction);
        menuMgr.add(qFoldAction);
        menuMgr.add(qDeleteAction);
        menuMgr.add(stripAction);

        MenuManager popupMenuMgr = new MenuManager();
        popupMenuMgr.add(qGotoAction);
        popupMenuMgr.add(qPushAllAction);
        popupMenuMgr.add(qPopAllAction);

        popupMenuMgr.add(new GroupMarker(IWorkbenchActionConstants.MB_ADDITIONS));
        getSite().registerContextMenu(popupMenuMgr, table.getTableViewer());

        Control control = table.getTableViewer().getControl();
        control.setMenu(popupMenuMgr.createContextMenu(control));
    }

    @Override
    public void setFocus() {
        populateTable();
    }

    public void populateTable() {
        Job job = new SafeUiJob(Messages.getString("PatchQueueView.jobName.populateTable")) { //$NON-NLS-1$
            @Override
            protected IStatus runSafe(IProgressMonitor monitor) {
                IStatus status = Status.OK_STATUS;
                if (hgRoot != null && !table.isDisposed()) {
                    try {
                        List<Patch> patches = HgQSeriesClient.getPatchesInSeries(hgRoot);

                        // TODO: We have to hide the popup menu if the table is empty or the team
                        // menu will be appended. This is a side effect of the team menu's
                        // locationURI being "popup:org.eclipse.ui.popup.any". If the team menu gets
                        // a proper locationURI, the next line can be deleted.
                        table.setEnabled(!patches.isEmpty());

                        topmostAppliedPatch = null;
                        for (Patch patch : patches) {
                            if (patch.isApplied()) {
                                topmostAppliedPatch = patch;
                            }
                        }

                        // Reverse so it's in the same order as the sync view
                        patches = new ArrayList<Patch>(patches);
                        Collections.reverse(patches);
                        table.setItems(patches);
                    } catch (HgException e) {
                        if (e.getCause() instanceof InterruptedException) {
                            // ?
                        } else {
                            showWarning(e.getConciseMessage());
                            MercurialEclipsePlugin.logError(e);
                            status = new Status(IStatus.ERROR, MercurialEclipsePlugin.ID,
                                    Messages.getString("PatchQueueView.cannotPopulatePatchViewTable"), e); //$NON-NLS-1$
                        }
                    } finally {
                        updateEnablement(StructuredSelection.EMPTY);
                    }
                }
                return status;
            }
        };

        job.setUser(false);
        job.setSystem(true);
        job.schedule();
    }

    /**
     * @see org.eclipse.ui.ISelectionListener#selectionChanged(org.eclipse.ui.IWorkbenchPart, org.eclipse.jface.viewers.ISelection)
     */
    public final void selectionChanged(IWorkbenchPart part, ISelection selection) {
        if (selection instanceof IStructuredSelection) {
            IStructuredSelection structured = (IStructuredSelection) selection;
            if (structured.getFirstElement() instanceof IAdaptable) {
                IResource newResource = (IResource) ((IAdaptable) structured.getFirstElement())
                        .getAdapter(IResource.class);

                if (newResource != null && newResource.isAccessible()
                        && MercurialTeamProvider.isHgTeamProviderFor(newResource)) {
                    rootSelected(MercurialTeamProvider.getHgRoot(newResource));
                }
            }
        }

        // TODO: why?
        if (part instanceof IEditorPart) {
            IEditorInput input = ((IEditorPart) part).getEditorInput();
            IFile file = (IFile) input.getAdapter(IFile.class);
            if (file != null && file.isAccessible() && MercurialTeamProvider.isHgTeamProviderFor(file)) {
                rootSelected(MercurialTeamProvider.getHgRoot(file));
            }
        }
    }

    /**
     * @see com.vectrace.MercurialEclipse.views.AbstractRootView#onRootChanged()
     */
    @Override
    protected void onRootChanged() {
        populateTable();
    }

    public static PatchQueueView getView() {
        PatchQueueView view = (PatchQueueView) MercurialEclipsePlugin.getActivePage().findView(ID);
        if (view == null) {
            try {
                view = (PatchQueueView) MercurialEclipsePlugin.getActivePage().showView(ID);
            } catch (PartInitException e) {
                MercurialEclipsePlugin.logError(e);
            }
        }
        return view;
    }

    /**
     * @see com.vectrace.MercurialEclipse.views.AbstractRootView#getDescription()
     */
    @Override
    protected String getDescription() {
        if (hgRoot == null) {
            return "No repository selected";
        }

        return Messages.getString("PatchQueueView.repository") + hgRoot;
    }

    private abstract class MQAction extends Action {
        protected final int refreshFlag;

        public MQAction(String name, int refreshFlag) {
            super(name);
            this.refreshFlag = refreshFlag;
        }

        public MQAction(String name, ImageDescriptor desc, int refreshFlag) {
            super(name, desc);
            this.refreshFlag = refreshFlag;
        }

        /**
         * @see org.eclipse.jface.action.Action#run()
         */
        @Override
        public final void run() {
            boolean changed = true;

            try {
                changed = invoke();
            } catch (HgException e) {
                MercurialEclipsePlugin.logError(e);
                showWarning(e.getConciseMessage());

                if (e.isMultiLine()) {
                    MercurialEclipsePlugin.showError(e);
                }
            } catch (CoreException e) {
                MercurialEclipsePlugin.logError(e);
                showWarning(e.getLocalizedMessage());
                MercurialEclipsePlugin.showError(e);
            } finally {
                if (changed) {
                    populateTable();
                    new RefreshWorkspaceStatusJob(hgRoot, refreshFlag).schedule();
                }
            }
        }

        public abstract boolean invoke() throws HgException, CoreException;
    }

    private abstract class MQPatchAction extends MQAction {

        private final String message;
        private final boolean requireSource;

        public MQPatchAction(String name, int refreshFlag, String message, boolean requireSource) {
            super(name, refreshFlag);

            this.message = message;
            this.requireSource = requireSource;
        }

        @Override
        public final boolean invoke() throws HgException, CoreException {
            try {
                return doInvoke();
            } catch (HgException e) {
                if (isPatchConflict(e)) {
                    try {
                        populateTable();
                        Job job = new RefreshWorkspaceStatusJob(hgRoot, refreshFlag);
                        job.schedule();
                        job.join();

                        new RejectsDialog(getSite().getShell(), hgRoot, e.getMessage(), "QRejectsDialog.title",
                                message, requireSource).open();
                        return false; // already refreshed and populated the table
                    } catch (HgException e2) {
                        MercurialEclipsePlugin.logError(e2);
                    } catch (InterruptedException e1) {
                        MercurialEclipsePlugin.logError(e1);
                    }
                }

                throw e;
            }
        }

        protected boolean isPatchConflict(HgException e) {
            return HgQPushClient.isPatchApplyConflict(e);
        }

        public abstract boolean doInvoke() throws HgException, CoreException;
    }
}