/******************************************************************************* * Copyright (c) 2008, 2010 Andrew Gvozdev. * 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: * Andrew Gvozdev (Quoin Inc.) - Initial implementation *******************************************************************************/ package org.eclipse.cdt.make.internal.ui.dnd; import java.lang.reflect.InvocationTargetException; import com.ibm.icu.text.MessageFormat; import java.util.ArrayList; import java.util.List; import org.eclipse.cdt.make.core.IMakeBuilderInfo; import org.eclipse.cdt.make.core.IMakeCommonBuildInfo; import org.eclipse.cdt.make.core.IMakeTarget; import org.eclipse.cdt.make.core.IMakeTargetManager; import org.eclipse.cdt.make.core.MakeCorePlugin; import org.eclipse.cdt.make.internal.ui.MakeUIPlugin; import org.eclipse.cdt.make.ui.dialogs.MakeTargetDialog; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.dialogs.MessageDialogWithToggle; import org.eclipse.jface.dialogs.ProgressMonitorDialog; import org.eclipse.jface.operation.IRunnableContext; import org.eclipse.jface.operation.IRunnableWithProgress; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.window.Window; import org.eclipse.swt.SWT; import org.eclipse.swt.SWTException; import org.eclipse.swt.dnd.DND; import org.eclipse.swt.widgets.Shell; /** * A collection of various functions for Make Target View drag and drop support. */ public class MakeTargetDndUtil { private static final int RENAME_ID = IDialogConstants.INTERNAL_ID; private static final int RENAME_TO_ALL_ID = IDialogConstants.INTERNAL_ID + 1; /** * The previous answer to the question of overwriting all targets. */ protected static int lastUserAnswer = IDialogConstants.YES_ID; /** * Default build command. */ public static final String DEFAULT_BUILD_COMMAND = "make"; //$NON-NLS-1$ /** * @param project - project where to get build command from * @return build command from project settings. */ public static String getProjectBuildCommand(IProject project) { IMakeTargetManager targetManager = MakeCorePlugin.getDefault().getTargetManager(); String[] targetBuilders = targetManager.getTargetBuilders(project); if (targetBuilders==null || targetBuilders.length==0) { return DEFAULT_BUILD_COMMAND; } String builderId = targetManager.getBuilderID(targetBuilders[0]); String buildCommand = DEFAULT_BUILD_COMMAND; try { IMakeBuilderInfo buildInfo = MakeCorePlugin.createBuildInfo(project, builderId); buildCommand = buildInfo.getBuildCommand().toString().trim(); } catch (CoreException e) { // keep default value } return buildCommand; } /** * Determine if the selection is allowed to be dragged from Make Target * View. It should be homogeneous having {@code IMakeTrget}s only and no * duplicate names are allowed. Make targets could be selected from * different folders. * * @param selection - make targets selection. * @return {@code true} if the selection is allowed to be dragged. */ public static boolean isDragable(ISelection selection) { if (selection instanceof IStructuredSelection) { List<?> selectedElements = ((IStructuredSelection) selection).toList(); if (selectedElements.size() == 0) { return false; } List<String> names = new ArrayList<String>(selectedElements.size()); for (Object element : selectedElements) { if (!(element instanceof IMakeTarget)) { return false; } String makeTargetName = ((IMakeTarget) element).getName(); for (String name : names) { if (makeTargetName == null || makeTargetName.equals(name)) { return false; } } names.add(makeTargetName); } return true; } return false; } /** * Perform action of copying or moving or dropping make targets to specified * container. This action displays a progress bar if user copies more than 1 * target. * * @param makeTargets - array of make targets to copy. * @param container - where to copy the selection. * @param operation - copying operation. Should be one of * {@link org.eclipse.swt.dnd.DND} operations. * @param shell - shell to display a progress bar. * * @see DND#DROP_NONE * @see DND#DROP_COPY * @see DND#DROP_MOVE * @see DND#DROP_LINK * @see DND#DROP_DEFAULT */ public static void copyTargets(IMakeTarget[] makeTargets, IContainer container, int operation, Shell shell) { if (makeTargets == null || makeTargets.length == 0 || container == null) { return; } lastUserAnswer = IDialogConstants.YES_ID; if (makeTargets.length == 1) { try { // Do not slow down generating modal window for a single target copyOneTarget(makeTargets[0], container, operation, shell, false); } catch (CoreException e) { // log any problem then ignore it MakeUIPlugin.log(e); } } else if (makeTargets.length > 1) { copyTargetsWithProgressIndicator(makeTargets, container, operation, shell); } } /** * Copy/move one make target to the specified container. * * @param makeTarget - make target. * @param container - container to copy/move to. * @param operation - copying operation. Should be one of * {@link org.eclipse.swt.dnd.DND} operations. * @param shell - shell to display user warnings. * @param offerOverwriteDialog - whether overwrite dialog is provided. * @throws CoreException on the failure of {@link IMakeTargetManager} or * {@link IMakeTarget} operation. * * @see DND#DROP_NONE * @see DND#DROP_COPY * @see DND#DROP_MOVE * @see DND#DROP_LINK * @see DND#DROP_DEFAULT */ public static void copyOneTarget(IMakeTarget makeTarget, IContainer container, final int operation, Shell shell, boolean offerOverwriteDialog) throws CoreException { IMakeTargetManager makeTargetManager = MakeCorePlugin.getDefault().getTargetManager(); IMakeTarget exists = makeTargetManager.findTarget(container, makeTarget.getName()); if (exists != null) { int userAnswer = IDialogConstants.CANCEL_ID; if (offerOverwriteDialog) { userAnswer = overwriteMakeTargetDialog(makeTarget.getName(), shell); } else { userAnswer = RENAME_ID; } if (userAnswer == IDialogConstants.YES_ID || userAnswer == IDialogConstants.YES_TO_ALL_ID) { copyTargetData(makeTarget, exists); if (operation == DND.DROP_MOVE) { makeTargetManager.removeTarget(makeTarget); } } else if (userAnswer == RENAME_ID || userAnswer == RENAME_TO_ALL_ID) { String name = generateUniqueName(makeTarget.getName(), container); IMakeTarget newMakeTarget = cloneTarget(name, makeTarget, container.getProject()); newMakeTarget.setContainer(container); int dialogReturnCode = Window.OK; if (userAnswer == RENAME_ID) { MakeTargetDialog dialog; try { dialog = new MakeTargetDialog(shell, newMakeTarget); dialogReturnCode = dialog.open(); } catch (CoreException e) { MakeUIPlugin.errorDialog(shell, MakeUIPlugin.getResourceString("AddBuildTargetAction.exception.internal"), e.toString(), e); //$NON-NLS-1$ } } else if (userAnswer == RENAME_TO_ALL_ID) { makeTargetManager.addTarget(container, newMakeTarget); } if (operation == DND.DROP_MOVE && dialogReturnCode != Window.CANCEL) { makeTargetManager.removeTarget(makeTarget); } } } else { makeTargetManager.addTarget(container, cloneTarget(makeTarget.getName(), makeTarget, container.getProject())); if (operation == DND.DROP_MOVE) { makeTargetManager.removeTarget(makeTarget); } } } /** * Generate a new unique non-existent name of the kind of "Copy (2) of name". * * @param targetName - name from where generate unique name. * @param container - container where the target belongs. * @return generated name. * @throws CoreException if {@code findTarget} having a problem. */ private static String generateUniqueName(String targetName, IContainer container) throws CoreException { IMakeTargetManager makeTargetManager = MakeCorePlugin.getDefault().getTargetManager(); // Try "name" String newName = targetName; if (makeTargetManager.findTarget(container, newName) == null) { return newName; } // Try "Copy of name" newName = MessageFormat.format(MakeUIPlugin.getResourceString("MakeTargetDnD.copyOf.uniqueName"), //$NON-NLS-1$ new Object[] { targetName }); if (makeTargetManager.findTarget(container, newName) == null) { return newName; } // Try "Copy (2) of name" for (int counter = 1;;counter++) { newName = MessageFormat.format(MakeUIPlugin.getResourceString("MakeTargetDnD.countedCopyOf.uniqueName"), //$NON-NLS-1$ new Object[] { counter, targetName }); if (makeTargetManager.findTarget(container, newName) == null) { return newName; } } } /** * Copy/move make targets to a given container. Displays progress bar. * * @param makeTargets - array of make targets to copy. * @param container - container to copy/move to. * @param operation - copying operation. Should be one of * {@link org.eclipse.swt.dnd.DND} operations. * @param shell - shell to display a progress bar. * * @see DND#DROP_NONE * @see DND#DROP_COPY * @see DND#DROP_MOVE * @see DND#DROP_LINK * @see DND#DROP_DEFAULT * */ private static void copyTargetsWithProgressIndicator(final IMakeTarget[] makeTargets, final IContainer container, final int operation, final Shell shell) { IRunnableWithProgress runnable = new IRunnableWithProgress() { public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { boolean isMove = operation == DND.DROP_MOVE; String textHeader = isMove ? MakeUIPlugin.getResourceString("MakeTargetDnD.moving") //$NON-NLS-1$ : MakeUIPlugin.getResourceString("MakeTargetDnD.copying"); //$NON-NLS-1$ String textAction = isMove ? MakeUIPlugin .getResourceString("MakeTargetDnD.moving.one") //$NON-NLS-1$ : MakeUIPlugin.getResourceString("MakeTargetDnD.copying.one"); //$NON-NLS-1$ monitor.beginTask(textHeader + ' ' + container.getName(), makeTargets.length - 1); for (IMakeTarget makeTarget : makeTargets) { if (makeTarget != null) { monitor.subTask(textAction + ' ' + makeTarget.getName()); try { copyOneTarget(makeTarget, container, operation, shell, true); } catch (CoreException e) { // log failures but ignore all targets which failed MakeUIPlugin.log(e); } if (lastUserAnswer == IDialogConstants.CANCEL_ID) { break; } } monitor.worked(1); if (monitor.isCanceled()) { break; } } monitor.done(); lastUserAnswer = IDialogConstants.YES_ID; } }; IRunnableContext context = new ProgressMonitorDialog(shell); try { context.run(false, true, runnable); } catch (InvocationTargetException e) { MakeUIPlugin.log(e); } catch (InterruptedException e) { MakeUIPlugin.log(e); } } /** * Overwrite Make Target dialog. * * @param name - name of make target to display to a user. * @param shell - shell where to display the dialog. * * @return user's answer. */ private static int overwriteMakeTargetDialog(String name, Shell shell) { if ( lastUserAnswer == IDialogConstants.YES_TO_ALL_ID || lastUserAnswer == IDialogConstants.NO_TO_ALL_ID || lastUserAnswer == RENAME_TO_ALL_ID ) { return lastUserAnswer; } String labels[] = new String[] { IDialogConstants.YES_LABEL, IDialogConstants.NO_LABEL, MakeUIPlugin.getResourceString("MakeTargetDnD.button.rename"), //$NON-NLS-1$ IDialogConstants.CANCEL_LABEL, }; String title = MakeUIPlugin.getResourceString("MakeTargetDnD.title.overwriteTargetConfirm"); //$NON-NLS-1$ String question = MessageFormat.format(MakeUIPlugin .getResourceString("MakeTargetDnD.message.overwriteTargetConfirm"), //$NON-NLS-1$ new Object[] { name }); String toggleApplyToAll = MakeUIPlugin.getResourceString("MakeTargetDnD.toggle.applyToAll"); //$NON-NLS-1$ MessageDialogWithToggle dialog = new MessageDialogWithToggle(shell, title, null, question, MessageDialog.QUESTION, labels, 0, toggleApplyToAll, false); try { dialog.open(); lastUserAnswer = dialog.getReturnCode(); boolean toAll = dialog.getToggleState(); if (toAll && lastUserAnswer==IDialogConstants.YES_ID) { lastUserAnswer = IDialogConstants.YES_TO_ALL_ID; } else if (toAll && lastUserAnswer==IDialogConstants.NO_ID) { lastUserAnswer = IDialogConstants.NO_TO_ALL_ID; } else if (toAll && lastUserAnswer==RENAME_ID) { lastUserAnswer = RENAME_TO_ALL_ID; } } catch (SWTException e) { MakeUIPlugin.log(e); lastUserAnswer = IDialogConstants.CANCEL_ID; } if (lastUserAnswer == SWT.DEFAULT) { // A window close returns SWT.DEFAULT, which has to be // mapped to a cancel lastUserAnswer = IDialogConstants.CANCEL_ID; } return lastUserAnswer; } /** * Creating a copy of IMakeTarget in a different project. * @param name - name of new target. * @param makeTarget - make target. * @param project - project where to assign the make target. * * @return newly created make target. * @throws CoreException if there is a problem with creating or copying the * target. */ private static IMakeTarget cloneTarget(String name, IMakeTarget makeTarget, IProject project) throws CoreException { IMakeTargetManager makeTargetManager = MakeCorePlugin.getDefault().getTargetManager(); String[] ids = makeTargetManager.getTargetBuilders(project); String builderId = ids[0]; IMakeTarget newMakeTarget = makeTargetManager.createTarget(project, name, builderId); copyTargetData(makeTarget, newMakeTarget); if (makeTarget.getName().equals(makeTarget.getBuildAttribute(IMakeTarget.BUILD_TARGET, ""))) { //$NON-NLS-1$ newMakeTarget.setBuildAttribute(IMakeTarget.BUILD_TARGET,name); } return newMakeTarget; } /** * Populate destination make target with data from source make target. * * @param source - source make target. * @param destination - destination make target. * @throws CoreException if there is a problem populating the target. * * See MakeTarget */ private static void copyTargetData(IMakeTarget source, IMakeTarget destination) throws CoreException { // IMakeTarget attributes // destination.project and destination.targetBuilderID are not changed destination.setRunAllBuilders(source.runAllBuilders()); destination.setAppendProjectEnvironment(source.appendProjectEnvironment()); destination.setBuildAttribute(IMakeTarget.BUILD_TARGET, source.getBuildAttribute(IMakeTarget.BUILD_TARGET, "")); //$NON-NLS-1$ // IMakeCommonBuildInfo attributes // Ignore IMakeCommonBuildInfo.BUILD_LOCATION in order not to pick // location of another project (or another folder) if (!source.isDefaultBuildCmd()){ destination.setBuildAttribute(IMakeCommonBuildInfo.BUILD_COMMAND, source.getBuildAttribute(IMakeCommonBuildInfo.BUILD_COMMAND, DEFAULT_BUILD_COMMAND)); destination.setBuildAttribute(IMakeCommonBuildInfo.BUILD_ARGUMENTS, source.getBuildAttribute(IMakeCommonBuildInfo.BUILD_ARGUMENTS, "")); //$NON-NLS-1$ } destination.setStopOnError(source.isStopOnError()); destination.setUseDefaultBuildCmd(source.isDefaultBuildCmd()); destination.setEnvironment(source.getEnvironment()); destination.setAppendEnvironment(source.appendEnvironment()); // setErrorParsers() is not supported in MakeTarget yet } /** * Create {@code MakeTarget} from basic data elements available during * copy/paste or drag/drop operations. The other data will be set to default. * * @param name - name of make target being created. * @param targetStr - build target. * @param command - make command. ("make" by default). * @param container - container where to place the target. * @return newly created {@link IMakeTarget}. * @throws CoreException if there was a problem creating new make target. */ public static IMakeTarget createMakeTarget(String name, String targetStr, String command, IContainer container) throws CoreException { IMakeTargetManager makeTargetManager = MakeCorePlugin.getDefault().getTargetManager(); IProject project = container.getProject(); String[] ids = makeTargetManager.getTargetBuilders(project); String builderId = ids[0]; // IMakeTarget attributes IMakeTarget newMakeTarget = makeTargetManager.createTarget(project, name, builderId); if (targetStr != null) { newMakeTarget.setBuildAttribute(IMakeTarget.BUILD_TARGET, targetStr); } // IMakeCommonBuildInfo attributes String projectBuildCommand = getProjectBuildCommand(project); if (command != null && command.length() > 0 && !command.equals(projectBuildCommand)) { newMakeTarget.setBuildAttribute(IMakeCommonBuildInfo.BUILD_COMMAND, command); newMakeTarget.setUseDefaultBuildCmd(false); } else { newMakeTarget.setBuildAttribute(IMakeCommonBuildInfo.BUILD_COMMAND, projectBuildCommand); newMakeTarget.setUseDefaultBuildCmd(true); } return newMakeTarget; } }