1 /******************************************************************************* 2 * Copyright (c) 2010, 2016 IBM Corporation and others. 3 * 4 * This program and the accompanying materials 5 * are made available under the terms of the Eclipse Public License 2.0 6 * which accompanies this distribution, and is available at 7 * https://www.eclipse.org/legal/epl-2.0/ 8 * 9 * SPDX-License-Identifier: EPL-2.0 10 * 11 * Contributors: 12 * Serge Beauchamp (Freescale Semiconductor) - initial API and implementation 13 * Mickael Istria (Red Hat Inc.) - Bug 486901 14 *******************************************************************************/ 15 package org.eclipse.ui.ide.dialogs; 16 17 import java.io.File; 18 19 import org.eclipse.core.resources.IContainer; 20 import org.eclipse.core.resources.IResource; 21 import org.eclipse.jface.dialogs.IDialogConstants; 22 import org.eclipse.jface.dialogs.TrayDialog; 23 import org.eclipse.jface.preference.IPreferenceStore; 24 import org.eclipse.swt.SWT; 25 import org.eclipse.swt.dnd.DND; 26 import org.eclipse.swt.events.SelectionAdapter; 27 import org.eclipse.swt.events.SelectionEvent; 28 import org.eclipse.swt.events.SelectionListener; 29 import org.eclipse.swt.layout.GridData; 30 import org.eclipse.swt.layout.GridLayout; 31 import org.eclipse.swt.widgets.Button; 32 import org.eclipse.swt.widgets.Composite; 33 import org.eclipse.swt.widgets.Control; 34 import org.eclipse.swt.widgets.Label; 35 import org.eclipse.swt.widgets.Link; 36 import org.eclipse.swt.widgets.Shell; 37 import org.eclipse.ui.PlatformUI; 38 import org.eclipse.ui.dialogs.PreferencesUtil; 39 import org.eclipse.ui.internal.ide.IDEInternalPreferences; 40 import org.eclipse.ui.internal.ide.IDEWorkbenchMessages; 41 import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin; 42 import org.eclipse.ui.internal.ide.IIDEHelpContextIds; 43 import org.eclipse.ui.internal.ide.dialogs.LinkedResourcesPreferencePage; 44 import org.eclipse.ui.internal.ide.dialogs.RelativePathVariableGroup; 45 46 47 /** 48 * Dialog to let the user customise how files and resources are created in a project 49 * hierarchy after the user drag and drop items on a workspace container. 50 * 51 * Files and folders can be created either by copying the source objects, creating 52 * linked resources, and/or creating virtual folders. 53 * @noextend This class is not intended to be subclassed by clients. 54 * @since 3.6 55 * 56 */ 57 public class ImportTypeDialog extends TrayDialog { 58 59 /** 60 * Copy the files and folders to the destination 61 */ 62 public static final int IMPORT_COPY = 1; 63 /** 64 * Import only files 65 */ 66 public static final int IMPORT_FILES_ONLY = 16; 67 /** 68 * Create linked resources for each file and folder 69 */ 70 public static final int IMPORT_LINK = 4; 71 /** 72 * Move the files and folders to the destination 73 */ 74 public static final int IMPORT_MOVE = 8; 75 /** 76 * Do not perform an import operation 77 */ 78 public static final int IMPORT_NONE = 0; 79 /** 80 * Recreate the file and folder hierarchy using groups and links 81 */ 82 public static final int IMPORT_VIRTUAL_FOLDERS_AND_LINKS = 2; 83 84 private Button copyButton = null; 85 86 private int currentSelection; 87 88 private Button linkButton = null; 89 90 private Button moveButton = null; 91 92 private int operationMask; 93 private String preferredVariable; 94 private IResource receivingResource = null; 95 private Button shadowCopyButton = null; 96 private String variable = null; 97 98 private RelativePathVariableGroup relativePathVariableGroup; 99 100 /** 101 * Creates the Import Type Dialog when resources are dragged and dropped from an Eclipse 102 * view. 103 * 104 * @param shell 105 * the parent Shell 106 * @param dropOperation 107 * The dropOperation that was used by the user 108 * @param sources 109 * The list of resources that were dragged 110 * @param target 111 * The target container onto which the resources were dropped 112 */ ImportTypeDialog(Shell shell, int dropOperation, IResource[] sources, IContainer target)113 public ImportTypeDialog(Shell shell, int dropOperation, 114 IResource[] sources, IContainer target) { 115 this(shell, selectAppropriateMask(dropOperation, sources, target), RelativePathVariableGroup.getPreferredVariable(sources, target)); 116 } 117 118 /** 119 * Creates the Import Type Dialog when files are dragged and dropped from the 120 * operating system's shell (Windows Explorer on Windows Platform, for example). 121 * 122 * @param shell 123 * the parent Shell 124 * @param dropOperation 125 * The dropOperation that was used by the user 126 * @param names 127 * The list of files that were dragged 128 * @param target 129 * The target container onto which the files were dropped 130 */ ImportTypeDialog(Shell shell, int dropOperation, String[] names, IContainer target)131 public ImportTypeDialog(Shell shell, int dropOperation, String[] names, IContainer target) { 132 this(shell, selectAppropriateMask(dropOperation, names, target), RelativePathVariableGroup.getPreferredVariable(names, target)); 133 } 134 135 /** 136 * @param parentShell 137 * @param operationMask 138 */ ImportTypeDialog(Shell parentShell, int operationMask, String preferredVariable)139 private ImportTypeDialog(Shell parentShell, int operationMask, String preferredVariable) { 140 super(parentShell); 141 142 this.preferredVariable = preferredVariable; 143 this.operationMask = operationMask; 144 currentSelection = 0; 145 String tmp = readContextPreference(IDEInternalPreferences.IMPORT_FILES_AND_FOLDERS_TYPE); 146 if (tmp.length() > 0) 147 currentSelection = Integer.parseInt(tmp); 148 currentSelection = currentSelection & operationMask; 149 if (currentSelection == 0) { 150 if (hasFlag(IMPORT_COPY)) 151 currentSelection = IMPORT_COPY; 152 else 153 currentSelection = IMPORT_MOVE; 154 } 155 156 IPreferenceStore store = IDEWorkbenchPlugin.getDefault().getPreferenceStore(); 157 if (store.getBoolean(IDEInternalPreferences.IMPORT_FILES_AND_FOLDERS_RELATIVE)) 158 variable = preferredVariable; 159 } 160 161 @Override close()162 public boolean close() { 163 return super.close(); 164 } 165 166 /** 167 * Get the user selection from the dialog. 168 * @return The current selection (one of IMPORT_COPY, IMPORT_VIRTUAL_FOLDERS_AND_LINKS, IMPORT_LINK and IMPORT_MOVE) 169 */ getSelection()170 public int getSelection() { 171 return currentSelection; 172 } 173 174 /** 175 * Get the selected variable if the selection is either IMPORT_VIRTUAL_FOLDERS_AND_LINKS or IMPORT_LINK 176 * @return The currently selected variable, or AUTOMATIC or ABSOLUTE_PATH 177 */ getVariable()178 public String getVariable() { 179 return variable; 180 } 181 182 /** Set the project that is the destination of the import operation 183 * @param resource the resource 184 */ setResource(IResource resource)185 public void setResource(IResource resource) { 186 receivingResource = resource; 187 } 188 hasFlag(int flag)189 private boolean hasFlag(int flag) { 190 return (operationMask & flag) != 0; 191 } 192 193 // the format of the context is operationMask,value:operationMask,value:operationMask,value readContextPreference(String key)194 private String readContextPreference(String key) { 195 String value = IDEWorkbenchPlugin.getDefault().getPreferenceStore().getString(key); 196 for (String keyPair : value.split(":")) { //$NON-NLS-1$ 197 String [] element = keyPair.split(","); //$NON-NLS-1$ 198 if (element.length == 2) { 199 if (element[0].equals(Integer.toString(operationMask))) 200 return element[1]; 201 } 202 } 203 return ""; //$NON-NLS-1$ 204 } 205 refreshSelection()206 private void refreshSelection() { 207 if (copyButton != null) 208 copyButton.setSelection(currentSelection == IMPORT_COPY); 209 if (shadowCopyButton != null) 210 shadowCopyButton.setSelection(currentSelection == IMPORT_VIRTUAL_FOLDERS_AND_LINKS); 211 if (linkButton != null) 212 linkButton.setSelection(currentSelection == IMPORT_LINK); 213 if (moveButton != null) 214 moveButton.setSelection(currentSelection == IMPORT_MOVE); 215 if (relativePathVariableGroup != null) { 216 relativePathVariableGroup.setEnabled((currentSelection & (IMPORT_VIRTUAL_FOLDERS_AND_LINKS | IMPORT_LINK)) != 0); 217 } 218 } 219 writeContextPreference(String key, String value)220 private void writeContextPreference(String key, String value) { 221 String oldValue = IDEWorkbenchPlugin.getDefault().getPreferenceStore().getString(key); 222 StringBuilder buffer = new StringBuilder(); 223 String [] keyPairs = oldValue.split(":"); //$NON-NLS-1$ 224 boolean found = false; 225 for (int i = 0; i < keyPairs.length; i++) { 226 if (i > 0) 227 buffer.append(":"); //$NON-NLS-1$ 228 String [] element = keyPairs[i].split(","); //$NON-NLS-1$ 229 if (element.length == 2) { 230 if (element[0].equals(Integer.toString(operationMask))) { 231 buffer.append(element[0] + "," + value); //$NON-NLS-1$ 232 found = true; 233 } 234 else 235 buffer.append(keyPairs[i]); 236 } 237 } 238 if (!found) { 239 if (buffer.length() > 0) 240 buffer.append(":"); //$NON-NLS-1$ 241 buffer.append(operationMask + "," + value); //$NON-NLS-1$ 242 } 243 String newValue = buffer.toString(); 244 IDEWorkbenchPlugin.getDefault().getPreferenceStore().setValue(key, newValue); 245 } 246 247 @Override buttonPressed(int buttonId)248 protected void buttonPressed(int buttonId) { 249 if (buttonId == IDialogConstants.OK_ID) { 250 writeContextPreference(IDEInternalPreferences.IMPORT_FILES_AND_FOLDERS_TYPE, Integer.toString(currentSelection)); 251 252 IPreferenceStore store = IDEWorkbenchPlugin.getDefault().getPreferenceStore(); 253 store.putValue(IDEInternalPreferences.IMPORT_FILES_AND_FOLDERS_RELATIVE, Boolean.toString(variable != null)); 254 } 255 super.buttonPressed(buttonId); 256 } 257 258 @Override configureShell(Shell shell)259 protected void configureShell(Shell shell) { 260 super.configureShell(shell); 261 String title = (operationMask & IMPORT_FILES_ONLY) != 0 ? IDEWorkbenchMessages.ImportTypeDialog_titleFilesOnly: 262 IDEWorkbenchMessages.ImportTypeDialog_title; 263 shell.setText(title); 264 PlatformUI.getWorkbench().getHelpSystem().setHelp(shell, 265 IIDEHelpContextIds.IMPORT_TYPE_DIALOG); 266 } 267 268 @Override createDialogArea(Composite parent)269 protected Control createDialogArea(Composite parent) { 270 boolean linkIsOnlyChoice = hasFlag(IMPORT_LINK) && !(hasFlag(IMPORT_COPY | IMPORT_MOVE) || (hasFlag(IMPORT_VIRTUAL_FOLDERS_AND_LINKS) && !hasFlag(IMPORT_FILES_ONLY))); 271 272 if (!linkIsOnlyChoice) 273 createMessageArea(parent); 274 Composite composite = new Composite(parent, 0); 275 GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true); 276 composite.setLayoutData(gridData); 277 composite.setFont(parent.getFont()); 278 279 280 GridLayout layout = new GridLayout(); 281 layout.numColumns = 1; 282 layout.marginWidth= convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_MARGIN); 283 layout.verticalSpacing= convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_SPACING); 284 layout.horizontalSpacing= convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_SPACING); 285 286 int indent= linkIsOnlyChoice ? 0: convertWidthInCharsToPixels(3); 287 288 layout.marginWidth += indent; 289 composite.setLayout(layout); 290 SelectionListener listener = new SelectionListener() { 291 @Override 292 public void widgetDefaultSelected(SelectionEvent e) { 293 currentSelection = ((Integer) e.widget.getData()).intValue(); 294 refreshSelection(); 295 } 296 297 @Override 298 public void widgetSelected(SelectionEvent e) { 299 currentSelection = ((Integer) e.widget.getData()).intValue(); 300 refreshSelection(); 301 } 302 }; 303 304 if (hasFlag(IMPORT_COPY)) { 305 copyButton = new Button(composite, SWT.RADIO); 306 copyButton.setText(hasFlag(IMPORT_FILES_ONLY) ? IDEWorkbenchMessages.ImportTypeDialog_copyFiles: IDEWorkbenchMessages.ImportTypeDialog_copyFilesAndDirectories); 307 gridData = new GridData(GridData.FILL_HORIZONTAL); 308 copyButton.setLayoutData(gridData); 309 copyButton.setData(IMPORT_COPY); 310 copyButton.addSelectionListener(listener); 311 copyButton.setFont(parent.getFont()); 312 } 313 314 if (hasFlag(IMPORT_MOVE)) { 315 moveButton = new Button(composite, SWT.RADIO); 316 moveButton.setText(hasFlag(IMPORT_FILES_ONLY) ? IDEWorkbenchMessages.ImportTypeDialog_moveFiles:IDEWorkbenchMessages.ImportTypeDialog_moveFilesAndDirectories); 317 gridData = new GridData(GridData.FILL_HORIZONTAL); 318 moveButton.setLayoutData(gridData); 319 moveButton.setData(IMPORT_MOVE); 320 moveButton.addSelectionListener(listener); 321 moveButton.setFont(parent.getFont()); 322 } 323 324 if (hasFlag(IMPORT_LINK) && !linkIsOnlyChoice) { 325 linkButton = new Button(composite, SWT.RADIO); 326 linkButton.setText(hasFlag(IMPORT_FILES_ONLY) ? IDEWorkbenchMessages.ImportTypeDialog_linkFiles:IDEWorkbenchMessages.ImportTypeDialog_createLinks); 327 gridData = new GridData(GridData.FILL_HORIZONTAL); 328 linkButton.setLayoutData(gridData); 329 linkButton.setData(IMPORT_LINK); 330 linkButton.addSelectionListener(listener); 331 linkButton.setFont(parent.getFont()); 332 } 333 334 if (hasFlag(IMPORT_VIRTUAL_FOLDERS_AND_LINKS) && !hasFlag(IMPORT_FILES_ONLY)) { 335 shadowCopyButton = new Button(composite, SWT.RADIO); 336 shadowCopyButton.setText(IDEWorkbenchMessages.ImportTypeDialog_recreateFilesAndDirectories); 337 gridData = new GridData(GridData.FILL_HORIZONTAL); 338 shadowCopyButton.setLayoutData(gridData); 339 shadowCopyButton.setData(IMPORT_VIRTUAL_FOLDERS_AND_LINKS); 340 shadowCopyButton.addSelectionListener(listener); 341 shadowCopyButton.setFont(parent.getFont()); 342 } 343 344 if (hasFlag(IMPORT_VIRTUAL_FOLDERS_AND_LINKS | IMPORT_LINK)) { 345 relativePathVariableGroup = new RelativePathVariableGroup(new RelativePathVariableGroup.IModel() { 346 @Override 347 public IResource getResource() { 348 return receivingResource; 349 } 350 @Override 351 public void setVariable(String string) { 352 variable = string; 353 } 354 @Override 355 public String getVariable() { 356 return variable; 357 } 358 }); 359 360 int groupIndent = 0; 361 362 if (!linkIsOnlyChoice) { 363 Button tmp = new Button(composite, SWT.CHECK); 364 tmp.setText("."); //$NON-NLS-1$ 365 groupIndent = tmp.computeSize(SWT.DEFAULT, SWT.DEFAULT).x; 366 tmp.dispose(); 367 368 Label tmpLabel = new Label(composite, SWT.NONE); 369 tmpLabel.setText("."); //$NON-NLS-1$ 370 groupIndent -= tmpLabel.computeSize(SWT.DEFAULT, SWT.DEFAULT).x; 371 tmpLabel.dispose(); 372 } 373 374 Composite variableGroup = new Composite(composite, 0); 375 gridData = new GridData(SWT.FILL, SWT.FILL, true, true); 376 gridData.horizontalIndent = groupIndent; 377 variableGroup.setLayoutData(gridData); 378 variableGroup.setFont(parent.getFont()); 379 380 layout = new GridLayout(); 381 layout.numColumns = 2; 382 layout.marginWidth= 0; 383 variableGroup.setLayout(layout); 384 385 relativePathVariableGroup.createContents(variableGroup); 386 relativePathVariableGroup.setSelection(variable != null); 387 if (variable != null) 388 relativePathVariableGroup.selectVariable(variable); 389 else 390 relativePathVariableGroup.selectVariable(preferredVariable); 391 } 392 393 if (linkIsOnlyChoice) { 394 currentSelection = IMPORT_LINK; 395 parent.getShell().setText(IDEWorkbenchMessages.ImportTypeDialog_titleFilesLinking); 396 } 397 createLinkControl(parent); 398 refreshSelection(); 399 return composite; 400 } 401 createLinkControl(Composite composite)402 private Control createLinkControl(Composite composite) { 403 Link link= new Link(composite, SWT.WRAP | SWT.RIGHT); 404 link.setText(IDEWorkbenchMessages.ImportTypeDialog_configureSettings); 405 link.addSelectionListener(new SelectionAdapter() { 406 @Override 407 public void widgetSelected(SelectionEvent e) { 408 openSettingsPage(); 409 } 410 }); 411 GridData gridData= new GridData(GridData.FILL, GridData.CENTER, true, false); 412 gridData.horizontalIndent = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_MARGIN); 413 link.setLayoutData(gridData); 414 link.setFont(composite.getFont()); 415 416 return link; 417 } 418 openSettingsPage()419 protected void openSettingsPage() { 420 String prefID = LinkedResourcesPreferencePage.PREF_ID; 421 PreferencesUtil.createPreferenceDialogOn(getShell(), prefID, new String[] {prefID}, null).open(); 422 } 423 createMessageArea(Composite parent)424 protected Control createMessageArea(Composite parent) { 425 Composite composite = new Composite(parent, 0); 426 GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true); 427 composite.setLayoutData(gridData); 428 composite.setFont(parent.getFont()); 429 430 431 GridLayout layout = new GridLayout(); 432 layout.numColumns = 1; 433 layout.marginTop= convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_MARGIN); 434 layout.marginWidth= convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_MARGIN); 435 composite.setLayout(layout); 436 437 String message = (operationMask & IMPORT_FILES_ONLY) != 0 ? IDEWorkbenchMessages.ImportTypeDialog_questionFilesOnly: 438 IDEWorkbenchMessages.ImportTypeDialog_question; 439 440 // create message 441 if (message != null) { 442 Label messageLabel = new Label(composite, SWT.WRAP); 443 messageLabel.setFont(parent.getFont()); 444 messageLabel.setText(message); 445 gridData = new GridData(SWT.FILL, SWT.BEGINNING, true, false); 446 messageLabel.setLayoutData(gridData); 447 } 448 return composite; 449 } 450 451 /** 452 * @param resources 453 * The list of items that were dragged 454 * @return true if a set of paths are files only or a mix of files and folders, false otherwise 455 */ areOnlyFiles(IResource[] resources)456 private static boolean areOnlyFiles(IResource[] resources) { 457 for (IResource resource : resources) { 458 if (resource.getType() != IResource.FILE) 459 return false; 460 } 461 return true; 462 } 463 464 /** 465 * @param names 466 * The list of items that were dragged 467 * @return true if a set of paths are files only or a mix of files and folders, false otherwise 468 */ areOnlyFiles(String[] names)469 private static boolean areOnlyFiles(String[] names) { 470 for (String name : names) { 471 File file = new File(name); 472 if (file.exists() && !file.isFile()) 473 return false; 474 } 475 return true; 476 } 477 478 /** 479 * Select the most appropriate mode that should be used for the dialog given 480 * the items dropped on the container, the container type, and the drop operation. 481 * 482 * @param dropOperation 483 * @param resources 484 * The list of items that were dragged 485 * @param target 486 * The target container onto which the items were dropped 487 * @return the appropriate import mask given the files dropped on the target 488 */ selectAppropriateMask(int dropOperation, IResource[] resources, IContainer target)489 private static int selectAppropriateMask(int dropOperation, IResource[] resources, IContainer target) { 490 int mask = ImportTypeDialog.IMPORT_VIRTUAL_FOLDERS_AND_LINKS | ImportTypeDialog.IMPORT_LINK; 491 if (!target.isVirtual() && (dropOperation != DND.DROP_LINK)) 492 mask |= ImportTypeDialog.IMPORT_COPY; 493 if (areOnlyFiles(resources)) 494 mask |= ImportTypeDialog.IMPORT_FILES_ONLY; 495 return mask; 496 } 497 498 /** 499 * Select the most appropriate mode that should be used for the dialog given 500 * the items dropped on the container, the container type, and the drop operation. 501 * 502 * @param dropOperation 503 * @param names 504 * The list of items that were dragged 505 * @param target 506 * The target container onto which the items were dropped 507 * @return the appropriate import mask given the files dropped on the target 508 */ selectAppropriateMask(int dropOperation, String[] names, IContainer target)509 private static int selectAppropriateMask(int dropOperation, String[] names, IContainer target) { 510 int mask = ImportTypeDialog.IMPORT_VIRTUAL_FOLDERS_AND_LINKS | ImportTypeDialog.IMPORT_LINK; 511 if (!target.isVirtual() && (dropOperation != DND.DROP_LINK)) 512 mask |= ImportTypeDialog.IMPORT_COPY; 513 if (areOnlyFiles(names)) 514 mask |= ImportTypeDialog.IMPORT_FILES_ONLY; 515 return mask; 516 } 517 } 518