1 /******************************************************************************* 2 * Copyright (c) 2000, 2018 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 * IBM Corporation - initial API and implementation 13 * Igor Fedorenko <igorfie@yahoo.com> - 14 * Fix for Bug 136921 [IDE] New File dialog locks for 20 seconds 15 *******************************************************************************/ 16 package org.eclipse.ui.internal.ide.misc; 17 18 import java.util.ArrayList; 19 import java.util.List; 20 21 import org.eclipse.core.resources.IContainer; 22 import org.eclipse.core.resources.ResourcesPlugin; 23 import org.eclipse.core.runtime.IPath; 24 import org.eclipse.core.runtime.Path; 25 import org.eclipse.equinox.bidi.StructuredTextTypeHandlerFactory; 26 import org.eclipse.jface.dialogs.Dialog; 27 import org.eclipse.jface.util.BidiUtils; 28 import org.eclipse.jface.viewers.ISelection; 29 import org.eclipse.jface.viewers.IStructuredSelection; 30 import org.eclipse.jface.viewers.StructuredSelection; 31 import org.eclipse.jface.viewers.TreeViewer; 32 import org.eclipse.jface.viewers.ViewerComparator; 33 import org.eclipse.osgi.util.TextProcessor; 34 import org.eclipse.swt.SWT; 35 import org.eclipse.swt.layout.GridData; 36 import org.eclipse.swt.layout.GridLayout; 37 import org.eclipse.swt.widgets.Composite; 38 import org.eclipse.swt.widgets.Event; 39 import org.eclipse.swt.widgets.Label; 40 import org.eclipse.swt.widgets.Listener; 41 import org.eclipse.swt.widgets.Text; 42 import org.eclipse.ui.internal.ide.IDEWorkbenchMessages; 43 import org.eclipse.ui.model.WorkbenchLabelProvider; 44 import org.eclipse.ui.part.DrillDownComposite; 45 46 /** 47 * Workbench-level composite for choosing a container. 48 */ 49 public class ContainerSelectionGroup extends Composite { 50 // The listener to notify of events 51 private Listener listener; 52 53 // Enable user to type in new container name 54 private boolean allowNewContainerName = true; 55 56 // show all projects by default 57 private boolean showClosedProjects = true; 58 59 // Last selection made by user 60 private IContainer selectedContainer; 61 62 // handle on parts 63 private Text containerNameField; 64 65 TreeViewer treeViewer; 66 67 // the message to display at the top of this dialog 68 private static final String DEFAULT_MSG_NEW_ALLOWED = IDEWorkbenchMessages.ContainerGroup_message; 69 70 private static final String DEFAULT_MSG_SELECT_ONLY = IDEWorkbenchMessages.ContainerGroup_selectFolder; 71 72 // sizing constants 73 private static final int SIZING_SELECTION_PANE_WIDTH = 320; 74 75 private static final int SIZING_SELECTION_PANE_HEIGHT = 300; 76 77 /** 78 * Creates a new instance of the widget. 79 * 80 * @param parent 81 * The parent widget of the group. 82 * @param listener 83 * A listener to forward events to. Can be null if no listener is 84 * required. 85 * @param allowNewContainerName 86 * Enable the user to type in a new container name instead of 87 * just selecting from the existing ones. 88 */ ContainerSelectionGroup(Composite parent, Listener listener, boolean allowNewContainerName)89 public ContainerSelectionGroup(Composite parent, Listener listener, 90 boolean allowNewContainerName) { 91 this(parent, listener, allowNewContainerName, null); 92 } 93 94 /** 95 * Creates a new instance of the widget. 96 * 97 * @param parent 98 * The parent widget of the group. 99 * @param listener 100 * A listener to forward events to. Can be null if no listener is 101 * required. 102 * @param allowNewContainerName 103 * Enable the user to type in a new container name instead of 104 * just selecting from the existing ones. 105 * @param message 106 * The text to present to the user. 107 */ ContainerSelectionGroup(Composite parent, Listener listener, boolean allowNewContainerName, String message)108 public ContainerSelectionGroup(Composite parent, Listener listener, 109 boolean allowNewContainerName, String message) { 110 this(parent, listener, allowNewContainerName, message, true); 111 } 112 113 /** 114 * Creates a new instance of the widget. 115 * 116 * @param parent 117 * The parent widget of the group. 118 * @param listener 119 * A listener to forward events to. Can be null if no listener is 120 * required. 121 * @param allowNewContainerName 122 * Enable the user to type in a new container name instead of 123 * just selecting from the existing ones. 124 * @param message 125 * The text to present to the user. 126 * @param showClosedProjects 127 * Whether or not to show closed projects. 128 */ ContainerSelectionGroup(Composite parent, Listener listener, boolean allowNewContainerName, String message, boolean showClosedProjects)129 public ContainerSelectionGroup(Composite parent, Listener listener, 130 boolean allowNewContainerName, String message, 131 boolean showClosedProjects) { 132 this(parent, listener, allowNewContainerName, message, 133 showClosedProjects, SIZING_SELECTION_PANE_HEIGHT, 134 SIZING_SELECTION_PANE_WIDTH); 135 } 136 137 /** 138 * Creates a new instance of the widget. 139 * 140 * @param parent 141 * The parent widget of the group. 142 * @param listener 143 * A listener to forward events to. Can be null if no listener is 144 * required. 145 * @param allowNewContainerName 146 * Enable the user to type in a new container name instead of 147 * just selecting from the existing ones. 148 * @param message 149 * The text to present to the user. 150 * @param showClosedProjects 151 * Whether or not to show closed projects. 152 * @param heightHint 153 * height hint for the drill down composite 154 * @param widthHint 155 * width hint for the drill down composite 156 */ ContainerSelectionGroup(Composite parent, Listener listener, boolean allowNewContainerName, String message, boolean showClosedProjects, int heightHint, int widthHint)157 public ContainerSelectionGroup(Composite parent, Listener listener, 158 boolean allowNewContainerName, String message, 159 boolean showClosedProjects, int heightHint, int widthHint) { 160 super(parent, SWT.NONE); 161 this.listener = listener; 162 this.allowNewContainerName = allowNewContainerName; 163 this.showClosedProjects = showClosedProjects; 164 if (message != null) { 165 createContents(message, heightHint, widthHint); 166 } else if (allowNewContainerName) { 167 createContents(DEFAULT_MSG_NEW_ALLOWED, heightHint, widthHint); 168 } else { 169 createContents(DEFAULT_MSG_SELECT_ONLY, heightHint, widthHint); 170 } 171 } 172 173 /** 174 * The container selection has changed in the tree view. Update the 175 * container name field value and notify all listeners. 176 * 177 * @param container 178 * The container that changed 179 */ containerSelectionChanged(IContainer container)180 public void containerSelectionChanged(IContainer container) { 181 selectedContainer = container; 182 183 if (allowNewContainerName) { 184 if (container == null) { 185 containerNameField.setText("");//$NON-NLS-1$ 186 } else { 187 String text = TextProcessor.process(container.getFullPath() 188 .makeRelative().toString()); 189 containerNameField.setText(text); 190 containerNameField.setToolTipText(text); 191 } 192 } 193 194 // fire an event so the parent can update its controls 195 if (listener != null) { 196 Event changeEvent = new Event(); 197 changeEvent.type = SWT.Selection; 198 changeEvent.widget = this; 199 listener.handleEvent(changeEvent); 200 } 201 } 202 203 /** 204 * Creates the contents of the composite. 205 * 206 * @param message 207 */ createContents(String message)208 public void createContents(String message) { 209 createContents(message, SIZING_SELECTION_PANE_HEIGHT, 210 SIZING_SELECTION_PANE_WIDTH); 211 } 212 213 /** 214 * Creates the contents of the composite. 215 * 216 * @param message 217 * @param heightHint 218 * @param widthHint 219 */ createContents(String message, int heightHint, int widthHint)220 public void createContents(String message, int heightHint, int widthHint) { 221 GridLayout layout = new GridLayout(); 222 layout.marginWidth = 0; 223 setLayout(layout); 224 setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); 225 226 Label label = new Label(this, SWT.WRAP); 227 label.setText(message); 228 label.setFont(this.getFont()); 229 230 if (allowNewContainerName) { 231 containerNameField = new Text(this, SWT.SINGLE | SWT.BORDER); 232 GridData gd = new GridData(GridData.FILL_HORIZONTAL); 233 gd.widthHint = widthHint; 234 containerNameField.setLayoutData(gd); 235 containerNameField.addListener(SWT.Modify, listener); 236 containerNameField.setFont(this.getFont()); 237 BidiUtils.applyBidiProcessing(containerNameField, StructuredTextTypeHandlerFactory.FILE); 238 } else { 239 // filler... 240 new Label(this, SWT.NONE); 241 } 242 243 createTreeViewer(heightHint); 244 Dialog.applyDialogFont(this); 245 } 246 247 /** 248 * Returns a new drill down viewer for this dialog. 249 * 250 * @param heightHint 251 * height hint for the drill down composite 252 */ createTreeViewer(int heightHint)253 protected void createTreeViewer(int heightHint) { 254 // Create drill down. 255 DrillDownComposite drillDown = new DrillDownComposite(this, SWT.BORDER); 256 GridData spec = new GridData(SWT.FILL, SWT.FILL, true, true); 257 spec.widthHint = SIZING_SELECTION_PANE_WIDTH; 258 spec.heightHint = heightHint; 259 drillDown.setLayoutData(spec); 260 261 // Create tree viewer inside drill down. 262 treeViewer = new TreeViewer(drillDown, SWT.NONE); 263 drillDown.setChildTree(treeViewer); 264 ContainerContentProvider cp = new ContainerContentProvider(); 265 cp.showClosedProjects(showClosedProjects); 266 treeViewer.setContentProvider(cp); 267 treeViewer.setLabelProvider(WorkbenchLabelProvider 268 .getDecoratingWorkbenchLabelProvider()); 269 treeViewer.setComparator(new ViewerComparator()); 270 treeViewer.setUseHashlookup(true); 271 treeViewer.addSelectionChangedListener(event -> { 272 IStructuredSelection selection = event.getStructuredSelection(); 273 containerSelectionChanged((IContainer) selection 274 .getFirstElement()); // allow null 275 }); 276 treeViewer.addDoubleClickListener(event -> { 277 ISelection selection = event.getSelection(); 278 if (selection instanceof IStructuredSelection) { 279 Object item = ((IStructuredSelection) selection) 280 .getFirstElement(); 281 if (item == null) { 282 return; 283 } 284 if (treeViewer.getExpandedState(item)) { 285 treeViewer.collapseToLevel(item, 1); 286 } else { 287 treeViewer.expandToLevel(item, 1); 288 } 289 } 290 }); 291 292 // This has to be done after the viewer has been laid out 293 treeViewer.setInput(ResourcesPlugin.getWorkspace()); 294 } 295 296 /** 297 * Returns the currently entered container name. Null if the field is empty. 298 * Note that the container may not exist yet if the user entered a new 299 * container name in the field. 300 * 301 * @return IPath 302 */ getContainerFullPath()303 public IPath getContainerFullPath() { 304 if (allowNewContainerName) { 305 String pathName = containerNameField.getText(); 306 if (pathName == null || pathName.length() < 1) { 307 return null; 308 } 309 // The user may not have made this absolute so do it for them 310 return (new Path(TextProcessor.deprocess(pathName))).makeAbsolute(); 311 312 } 313 if (selectedContainer == null) 314 return null; 315 return selectedContainer.getFullPath(); 316 317 } 318 319 /** 320 * Gives focus to one of the widgets in the group, as determined by the 321 * group. 322 */ setInitialFocus()323 public void setInitialFocus() { 324 if (allowNewContainerName) { 325 containerNameField.setFocus(); 326 } else { 327 treeViewer.getTree().setFocus(); 328 } 329 } 330 331 /** 332 * Sets the selected existing container. 333 * 334 * @param container 335 */ setSelectedContainer(IContainer container)336 public void setSelectedContainer(IContainer container) { 337 selectedContainer = container; 338 339 // expand to and select the specified container 340 List<IContainer> itemsToExpand = new ArrayList<>(); 341 IContainer parent = container.getParent(); 342 while (parent != null) { 343 itemsToExpand.add(0, parent); 344 parent = parent.getParent(); 345 } 346 treeViewer.setExpandedElements(itemsToExpand.toArray()); 347 treeViewer.setSelection(new StructuredSelection(container), true); 348 } 349 } 350