Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 135321b5871ff669a87919b5ac7496a7880ad035 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
/*******************************************************************************
 * Copyright (c) 2008, 2011 Andrew Gvozdev.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *    Andrew Gvozdev (Quoin Inc.) - Initial implementation
 *******************************************************************************/

package org.eclipse.cdt.make.internal.ui.dnd;

import java.lang.reflect.InvocationTargetException;
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;

import com.ibm.icu.text.MessageFormat;

/**
 * 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<>(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(), //$NON-NLS-1$
								e);
					}
				} 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() {
			@Override
			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;
	}
}

Back to the top