/*
* Copyright (c) 2012, 2013, 2015, 2016 Eike Stepper (Berlin, Germany) 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:
* Eike Stepper - initial API and implementation
*/
package org.eclipse.emf.cdo.ui.internal.team.history;
import org.eclipse.emf.cdo.common.branch.CDOBranch;
import org.eclipse.emf.cdo.common.branch.CDOBranchPoint;
import org.eclipse.emf.cdo.common.commit.CDOCommitInfo;
import org.eclipse.emf.cdo.common.commit.CDOCommitInfoHandler;
import org.eclipse.emf.cdo.common.commit.CDOCommitInfoManager;
import org.eclipse.emf.cdo.eresource.CDOResourceFolder;
import org.eclipse.emf.cdo.internal.ui.history.Net;
import org.eclipse.emf.cdo.internal.ui.history.NetRenderer;
import org.eclipse.emf.cdo.internal.ui.history.Track;
import org.eclipse.emf.cdo.session.CDOSession;
import org.eclipse.emf.cdo.spi.common.branch.CDOBranchUtil;
import org.eclipse.emf.cdo.transaction.CDOMerger;
import org.eclipse.emf.cdo.transaction.CDOTransaction;
import org.eclipse.emf.cdo.ui.widgets.CommitHistoryComposite;
import org.eclipse.emf.cdo.ui.widgets.CommitHistoryComposite.Input;
import org.eclipse.emf.cdo.ui.widgets.CommitHistoryComposite.Input.IllegalInputException;
import org.eclipse.emf.cdo.ui.widgets.CommitHistoryComposite.LabelProvider;
import org.eclipse.emf.cdo.util.CDOUtil;
import org.eclipse.net4j.util.AdapterUtil;
import org.eclipse.net4j.util.ReflectUtil;
import org.eclipse.net4j.util.event.IListener;
import org.eclipse.net4j.util.lifecycle.ILifecycle;
import org.eclipse.net4j.util.lifecycle.LifecycleEventAdapter;
import org.eclipse.net4j.util.lifecycle.LifecycleUtil;
import org.eclipse.net4j.util.om.OMPlatform;
import org.eclipse.net4j.util.ui.UIUtil;
import org.eclipse.net4j.util.ui.widgets.StackComposite;
import org.eclipse.emf.spi.cdo.DefaultCDOMerger;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.util.LocalSelectionTransfer;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.ViewerDropAdapter;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.dnd.TransferData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.team.ui.history.HistoryPage;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.part.IPageSite;
/**
* @author Eike Stepper
*/
public class CDOHistoryPage extends HistoryPage
{
private static final String POPUP_ID = "#PopupMenu";
private static final boolean DEBUG = OMPlatform.INSTANCE.isProperty("org.eclipse.emf.cdo.ui.team.history.debug");
private static final boolean TEST = OMPlatform.INSTANCE.isProperty("org.eclipse.emf.cdo.ui.team.history.test");
private StackComposite stackComposite;
private Control offlineControl;
private CommitHistoryComposite commitHistoryComposite;
private Input input;
private IListener inputListener = new LifecycleEventAdapter()
{
@Override
protected void onDeactivated(ILifecycle lifecycle)
{
if (!commitHistoryComposite.isDisposed())
{
commitHistoryComposite.getDisplay().asyncExec(new Runnable()
{
public void run()
{
if (!commitHistoryComposite.isDisposed())
{
CDOHistoryPage.this.setInput(null);
}
}
});
}
}
};
public CDOHistoryPage()
{
}
public String getName()
{
return input != null ? input.toString() : null;
}
public String getDescription()
{
return "";
}
@Override
public Control getControl()
{
return stackComposite;
}
@Override
public void createControl(Composite parent)
{
stackComposite = new StackComposite(parent, SWT.NONE);
Label label = new Label(stackComposite, SWT.NONE);
label.setText("The selected session belongs to an offline workspace.\nThe remote history cannot be provided.");
label.setForeground(label.getDisplay().getSystemColor(SWT.COLOR_DARK_BLUE));
offlineControl = label;
commitHistoryComposite = new CommitHistoryComposite(stackComposite, SWT.NONE)
{
@Override
protected void doubleClicked(CDOCommitInfo commitInfo)
{
if (TEST)
{
new TransactionalBranchPointOperation()
{
@Override
protected void run(CDOTransaction transaction)
{
CDOResourceFolder folder = transaction.getOrCreateResourceFolder("test");
folder.addResource("resource-" + folder.getNodes().size());
}
}.execute(commitInfo);
}
}
};
stackComposite.setTopControl(commitHistoryComposite);
IPageSite site = getSite();
final TableViewer tableViewer = commitHistoryComposite.getTableViewer();
UIUtil.addDragSupport(tableViewer);
if (TEST)
{
tableViewer.addDropSupport(DND.DROP_MOVE, new Transfer[] { LocalSelectionTransfer.getTransfer() }, new ViewerDropAdapter(tableViewer)
{
{
// We don't want it to look like you can insert new elements, only drop onto existing elements.
setFeedbackEnabled(false);
}
@Override
public boolean validateDrop(Object target, int operation, TransferData transferType)
{
if (target instanceof CDOBranchPoint && LocalSelectionTransfer.getTransfer().isSupportedType(transferType))
{
CDOBranchPoint objectToDrop = getObjectToDrop(transferType);
if (objectToDrop != null)
{
if (CDOBranchUtil.isContainedBy(objectToDrop, (CDOBranchPoint)target))
{
return false;
}
return true;
}
}
return false;
}
@Override
public boolean performDrop(Object data)
{
final CDOBranchPoint objectToDrop = UIUtil.getElement((ISelection)data, CDOBranchPoint.class);
final CDOBranchPoint dropTarget = (CDOBranchPoint)getCurrentTarget();
boolean result = new TransactionalBranchPointOperation()
{
@Override
protected void run(CDOTransaction transaction)
{
CDOMerger merger = new DefaultCDOMerger.PerFeature.ManyValued();
transaction.merge(objectToDrop, merger);
}
}.execute(dropTarget);
if (result)
{
tableViewer.getControl().setFocus();
tableViewer.setSelection(new StructuredSelection(dropTarget));
}
return result;
}
private CDOBranchPoint getObjectToDrop(TransferData transferType)
{
return UIUtil.getElement(LocalSelectionTransfer.getTransfer().getSelection(), CDOBranchPoint.class);
}
});
}
if (DEBUG)
{
((LabelProvider)tableViewer.getLabelProvider()).setFormatTimeStamps(false);
}
MenuManager menuManager = new MenuManager(POPUP_ID);
menuManager.add(new Separator("compare"));
menuManager.add(new Separator("branching"));
menuManager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
Menu menu = menuManager.createContextMenu(tableViewer.getControl());
tableViewer.getControl().setMenu(menu);
site.registerContextMenu(POPUP_ID, menuManager, tableViewer);
site.setSelectionProvider(tableViewer);
IActionBars actionBars = site.getActionBars();
setupToolBar(actionBars.getToolBarManager());
setupViewMenu(actionBars.getMenuManager());
}
@Override
public void setFocus()
{
commitHistoryComposite.setFocus();
}
public void refresh()
{
commitHistoryComposite.refreshLayout(true);
}
@SuppressWarnings({ "rawtypes", "unchecked" })
public Object getAdapter(Class adapter)
{
return AdapterUtil.adapt(this, adapter, false);
}
public boolean isValidInput(Object object)
{
return canShowHistoryFor(object);
}
@Override
public boolean inputSet()
{
if (input != null)
{
input.removeListener(inputListener);
input.deactivate();
input = null;
}
Object object = getInput();
try
{
input = new CommitHistoryComposite.Input(object);
input.addListener(inputListener);
input.activate();
if (input.isOffline())
{
stackComposite.setTopControl(offlineControl);
}
else
{
stackComposite.setTopControl(commitHistoryComposite);
}
return true;
}
catch (Exception ex)
{
return false;
}
finally
{
commitHistoryComposite.setInput(input);
}
}
@Override
public void dispose()
{
if (input != null)
{
input.deactivate();
input = null;
}
super.dispose();
}
protected void setupToolBar(IToolBarManager manager)
{
if (DEBUG)
{
Action action = new Action("DEBUG", IAction.AS_PUSH_BUTTON)
{
@SuppressWarnings("unused")
@Override
public void run()
{
NetRenderer netRenderer = (NetRenderer)ReflectUtil.getValue(ReflectUtil.getField(CommitHistoryComposite.class, "netRenderer"),
commitHistoryComposite);
Net net = netRenderer.getNet();
Track[] tracks = net.getTracks();
CDOSession session = net.getSession();
System.out.println("Debug " + net); // Set a breakpoint on this line to inspect the net.
}
};
ISharedImages sharedImages = PlatformUI.getWorkbench().getSharedImages();
action.setImageDescriptor(sharedImages.getImageDescriptor(ISharedImages.IMG_OBJS_INFO_TSK));
manager.add(action);
}
}
protected void setupViewMenu(IMenuManager manager)
{
manager.add(new TableRedrawingAction("Format Time Stamps", SWT.CHECK)
{
@Override
protected boolean getInitialCheckState(LabelProvider labelProvider)
{
return labelProvider.isFormatTimeStamps();
}
@Override
protected void doRun(LabelProvider labelProvider)
{
labelProvider.setFormatTimeStamps(!labelProvider.isFormatTimeStamps());
}
});
manager.add(new TableRedrawingAction("Shorten Branch Paths", SWT.CHECK)
{
@Override
protected boolean getInitialCheckState(LabelProvider labelProvider)
{
return labelProvider.isShortenBranchPaths();
}
@Override
protected void doRun(LabelProvider labelProvider)
{
labelProvider.setShortenBranchPaths(!labelProvider.isShortenBranchPaths());
}
});
}
public static boolean canShowHistoryFor(Object object)
{
if (object == null)
{
return false;
}
try
{
new CommitHistoryComposite.Input(object);
return true;
}
catch (IllegalInputException ex)
{
return false;
}
}
/**
* @author Eike Stepper
*/
private abstract class TableRedrawingAction extends Action
{
private final LabelProvider labelProvider = commitHistoryComposite.getLabelProvider();
public TableRedrawingAction(String text, int style)
{
super(text, style);
setChecked(getInitialCheckState(labelProvider));
}
@Override
public void run()
{
doRun(labelProvider);
commitHistoryComposite.getTableViewer().getTable().redraw();
}
protected abstract void doRun(LabelProvider labelProvider);
protected abstract boolean getInitialCheckState(LabelProvider labelProvider);
}
/**
* @author Eike Stepper
*/
private abstract class TransactionalBranchPointOperation
{
public boolean execute(CDOBranchPoint branchPoint)
{
CDOTransaction transaction = null;
try
{
CDOSession session = input.getSession();
CDOBranch branch = branchPoint.getBranch();
final long[] lastCommitTime = { 0 };
CDOCommitInfoManager commitInfoManager = session.getCommitInfoManager();
commitInfoManager.getCommitInfos(branch, Long.MAX_VALUE, null, null, -1, new CDOCommitInfoHandler()
{
public void handleCommitInfo(CDOCommitInfo commitInfo)
{
lastCommitTime[0] = commitInfo.getTimeStamp();
}
});
long timeStamp = branchPoint.getTimeStamp();
if (timeStamp != lastCommitTime[0])
{
String name = "branch" + (timeStamp - session.getRepositoryInfo().getCreationTime()) / 1000;
branch = branch.createBranch(name, timeStamp);
}
transaction = session.openTransaction(branch);
CDOUtil.configureView(transaction);
try
{
run(transaction);
transaction.commit();
return true;
}
catch (Throwable ex)
{
ex.printStackTrace();
}
}
catch (Exception ex)
{
ex.printStackTrace();
}
finally
{
LifecycleUtil.deactivate(transaction);
}
return false;
}
protected abstract void run(CDOTransaction transaction) throws Exception;
}
}