Update org.apache.commons:commons-compress to 1.25.0
[egit/eclipse.git] / org.eclipse.egit.ui / src / org / eclipse / egit / ui / internal / clone / GitCloneDropAdapter.java
blob2667a68d866550adf2e8409723cc82effdc09f06
1 /*******************************************************************************
2 * Copyright (c) 2011, 2020 The Eclipse Foundation and others.
4 * All rights reserved. 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/
9 * SPDX-License-Identifier: EPL-2.0
11 * Contributors:
12 * The Eclipse Foundation - initial API and implementation
13 * Ian Pun - reimplemented to work with Git Cloning DND using MarketplaceDropAdapter
14 *******************************************************************************/
15 package org.eclipse.egit.ui.internal.clone;
17 import java.util.Collections;
19 import org.eclipse.core.runtime.IProgressMonitor;
20 import org.eclipse.core.runtime.IStatus;
21 import org.eclipse.core.runtime.Status;
22 import org.eclipse.core.runtime.jobs.Job;
23 import org.eclipse.e4.ui.workbench.UIEvents;
24 import org.eclipse.egit.ui.internal.CommonUtils;
25 import org.eclipse.egit.ui.internal.repository.tree.command.CloneCommand;
26 import org.eclipse.jface.util.Util;
27 import org.eclipse.swt.dnd.DND;
28 import org.eclipse.swt.dnd.DropTarget;
29 import org.eclipse.swt.dnd.DropTargetAdapter;
30 import org.eclipse.swt.dnd.DropTargetEvent;
31 import org.eclipse.swt.dnd.DropTargetListener;
32 import org.eclipse.swt.dnd.Transfer;
33 import org.eclipse.swt.dnd.TransferData;
34 import org.eclipse.swt.dnd.URLTransfer;
35 import org.eclipse.swt.widgets.Composite;
36 import org.eclipse.swt.widgets.Control;
37 import org.eclipse.swt.widgets.Display;
38 import org.eclipse.swt.widgets.Shell;
39 import org.eclipse.ui.IPageListener;
40 import org.eclipse.ui.IPartListener2;
41 import org.eclipse.ui.IPartService;
42 import org.eclipse.ui.IPerspectiveDescriptor;
43 import org.eclipse.ui.IPerspectiveListener;
44 import org.eclipse.ui.IWindowListener;
45 import org.eclipse.ui.IWorkbench;
46 import org.eclipse.ui.IWorkbenchPage;
47 import org.eclipse.ui.IWorkbenchPartReference;
48 import org.eclipse.ui.IWorkbenchWindow;
49 import org.eclipse.ui.PlatformUI;
50 import org.eclipse.ui.progress.UIJob;
51 import org.osgi.service.component.annotations.Component;
52 import org.osgi.service.event.Event;
53 import org.osgi.service.event.EventConstants;
54 import org.osgi.service.event.EventHandler;
56 /**
57 * Adapter to listen for any Drag and Drop operations that transfer a valid git
58 * URL. If it goes through the URL parser correctly, a Clone Git Repo wizard
59 * will appear and be populated.
61 @Component(property = EventConstants.EVENT_TOPIC + '='
62 + UIEvents.UILifeCycle.APP_STARTUP_COMPLETE)
63 public class GitCloneDropAdapter implements EventHandler {
65 private static final int[] PREFERRED_DROP_OPERATIONS = { DND.DROP_DEFAULT,
66 DND.DROP_COPY, DND.DROP_MOVE, DND.DROP_LINK };
68 private static final int DROP_OPERATIONS = DND.DROP_MOVE | DND.DROP_COPY
69 | DND.DROP_LINK | DND.DROP_DEFAULT;
71 private final DropTargetAdapter dropListener = new GitDropTargetListener();
73 private final WorkbenchListener workbenchListener = new WorkbenchListener();
75 private Transfer[] transferAgents;
77 @Override
78 public void handleEvent(Event event) {
79 UIJob registerJob = new UIJob(PlatformUI.getWorkbench().getDisplay(),
80 "Git Clone DND Initialization") { //$NON-NLS-1$
82 setPriority(Job.SHORT);
83 setSystem(true);
86 @Override
87 public IStatus runInUIThread(IProgressMonitor monitor) {
88 IWorkbench workbench = PlatformUI.getWorkbench();
89 workbench.addWindowListener(workbenchListener);
90 IWorkbenchWindow[] workbenchWindows = workbench
91 .getWorkbenchWindows();
92 for (IWorkbenchWindow window : workbenchWindows) {
93 workbenchListener.hookWindow(window);
95 return Status.OK_STATUS;
99 registerJob.schedule();
102 private void installDropTarget(final Shell shell) {
103 hookUrlTransfer(shell, dropListener);
106 private DropTarget hookUrlTransfer(final Shell shell,
107 DropTargetAdapter dropAdapter) {
108 DropTarget target = findDropTarget(shell);
109 if (target != null) {
110 // target exists, get it and check proper registration
111 registerWithExistingTarget(target);
112 } else {
113 target = new DropTarget(shell, DROP_OPERATIONS);
114 if (transferAgents == null) {
115 transferAgents = new Transfer[] { URLTransfer.getInstance() };
117 target.setTransfer(transferAgents);
119 registerDropListener(target, dropAdapter);
121 Control[] children = shell.getChildren();
122 for (Control child : children) {
123 hookRecursive(child, dropAdapter);
125 return target;
128 private void registerDropListener(DropTarget target,
129 DropTargetListener dropAdapter) {
130 target.removeDropListener(dropAdapter);
131 target.addDropListener(dropAdapter);
134 private void hookRecursive(Control child, DropTargetListener dropAdapter) {
135 DropTarget childTarget = findDropTarget(child);
136 if (childTarget != null) {
137 registerWithExistingTarget(childTarget);
138 registerDropListener(childTarget, dropAdapter);
140 if (child instanceof Composite) {
141 Composite composite = (Composite) child;
142 Control[] children = composite.getChildren();
143 for (Control control : children) {
144 hookRecursive(control, dropAdapter);
149 private void registerWithExistingTarget(DropTarget target) {
150 Transfer[] transfers = target.getTransfer();
151 if (transfers != null) {
152 for (Transfer transfer : transfers) {
153 if (transfer instanceof URLTransfer) {
154 return;
157 Transfer[] newTransfers = new Transfer[transfers.length + 1];
158 System.arraycopy(transfers, 0, newTransfers, 0, transfers.length);
159 newTransfers[transfers.length] = URLTransfer.getInstance();
160 target.setTransfer(newTransfers);
164 private DropTarget findDropTarget(Control control) {
165 Object object = control.getData(DND.DROP_TARGET_KEY);
166 if (object instanceof DropTarget) {
167 return (DropTarget) object;
169 return null;
173 * @param url
175 protected void proceedClone(String url) {
176 CommonUtils.runCommand(CloneCommand.COMMAND_ID, null, Collections
177 .singletonMap(CloneCommand.REPOSITORY_URI_PARAMETER_ID, url));
180 private class GitDropTargetListener extends DropTargetAdapter {
182 @Override
183 public void dragEnter(DropTargetEvent e) {
184 updateDragDetails(e);
187 @Override
188 public void dragOver(DropTargetEvent e) {
189 updateDragDetails(e);
192 @Override
193 public void dragLeave(DropTargetEvent e) {
194 if (e.detail == DND.DROP_NONE) {
195 setDropOperation(e);
199 @Override
200 public void dropAccept(DropTargetEvent e) {
201 updateDragDetails(e);
204 @Override
205 public void dragOperationChanged(DropTargetEvent e) {
206 updateDragDetails(e);
209 private void setDropOperation(DropTargetEvent e) {
210 int allowedOperations = e.operations;
211 for (int op : PREFERRED_DROP_OPERATIONS) {
212 if ((allowedOperations & op) != 0) {
213 e.detail = op;
214 return;
217 e.detail = allowedOperations;
220 private void updateDragDetails(DropTargetEvent e) {
221 if (dropTargetIsValid(e, false)) {
222 setDropOperation(e);
226 private boolean dropTargetIsValid(DropTargetEvent e, boolean isDrop) {
227 if (URLTransfer.getInstance().isSupportedType(e.currentDataType)) {
228 // on Windows, we get the URL already during drag operations...
229 // FIXME find a way to check the URL early on other platforms,
230 // too...
231 if (isDrop || Util.isWindows()) {
232 if (e.data == null && !extractEventData(e)) {
233 // ... but if we don't, it's no problem, unless this is
234 // already the final drop event
235 return !isDrop;
237 final String url = getUrl(e.data);
238 if (!GitUrlChecker.isValidGitUrl(url)) {
239 return false;
242 return true;
244 return false;
247 private boolean extractEventData(DropTargetEvent e) {
248 TransferData transferData = e.currentDataType;
249 if (transferData != null) {
250 Object data = URLTransfer.getInstance()
251 .nativeToJava(transferData);
252 if (data != null && getUrl(data) != null) {
253 e.data = data;
254 return true;
257 return false;
260 @Override
261 public void drop(DropTargetEvent event) {
262 if (!URLTransfer.getInstance()
263 .isSupportedType(event.currentDataType)) {
264 // ignore
265 return;
267 if (event.data == null) {
268 // reject
269 event.detail = DND.DROP_NONE;
270 return;
272 if (!dropTargetIsValid(event, true)) {
273 // reject
274 event.detail = DND.DROP_NONE;
275 return;
277 final String url = getUrl(event.data);
278 DropTarget source = (DropTarget) event.getSource();
279 Display display = source.getDisplay();
280 display.asyncExec(new Runnable() {
281 @Override
282 public void run() {
283 proceedClone(url);
289 private String getUrl(Object eventData) {
290 if (!(eventData instanceof String)) {
291 return null;
293 // Depending on the form the link and browser/os,
294 // we get the url twice in the data separated by new lines
295 String[] dataLines = ((String) eventData)
296 .split(System.lineSeparator());
297 String url = dataLines[0];
298 return url;
302 private class WorkbenchListener implements IPartListener2, IPageListener,
303 IPerspectiveListener, IWindowListener {
305 @Override
306 public void perspectiveActivated(IWorkbenchPage page,
307 IPerspectiveDescriptor perspective) {
308 pageChanged(page);
311 @Override
312 public void perspectiveChanged(IWorkbenchPage page,
313 IPerspectiveDescriptor perspective, String changeId) {
314 // Nothing to do
317 @Override
318 public void pageActivated(IWorkbenchPage page) {
319 pageChanged(page);
322 @Override
323 public void pageClosed(IWorkbenchPage page) {
324 // Nothing to do
327 @Override
328 public void pageOpened(IWorkbenchPage page) {
329 pageChanged(page);
332 private void pageChanged(IWorkbenchPage page) {
333 if (page == null) {
334 return;
336 IWorkbenchWindow workbenchWindow = page.getWorkbenchWindow();
337 windowChanged(workbenchWindow);
340 @Override
341 public void windowActivated(IWorkbenchWindow window) {
342 windowChanged(window);
345 private void windowChanged(IWorkbenchWindow window) {
346 if (window == null) {
347 return;
349 Shell shell = window.getShell();
350 runUpdate(shell);
353 @Override
354 public void windowDeactivated(IWorkbenchWindow window) {
355 // Nothing to do
358 @Override
359 public void windowClosed(IWorkbenchWindow window) {
360 // Nothing to do
363 @Override
364 public void windowOpened(IWorkbenchWindow window) {
365 hookWindow(window);
368 public void hookWindow(IWorkbenchWindow window) {
369 if (window == null) {
370 return;
372 window.addPageListener(this);
373 window.addPerspectiveListener(this);
374 IPartService partService = window.getService(IPartService.class);
375 partService.addPartListener(this);
376 windowChanged(window);
379 @Override
380 public void partOpened(IWorkbenchPartReference partRef) {
381 partUpdate(partRef);
384 @Override
385 public void partActivated(IWorkbenchPartReference partRef) {
386 partUpdate(partRef);
389 @Override
390 public void partBroughtToTop(IWorkbenchPartReference partRef) {
391 partUpdate(partRef);
394 @Override
395 public void partVisible(IWorkbenchPartReference partRef) {
396 // Nothing to do
399 @Override
400 public void partClosed(IWorkbenchPartReference partRef) {
401 partUpdate(partRef);
404 @Override
405 public void partDeactivated(IWorkbenchPartReference partRef) {
406 partUpdate(partRef);
409 @Override
410 public void partHidden(IWorkbenchPartReference partRef) {
411 partUpdate(partRef);
414 @Override
415 public void partInputChanged(IWorkbenchPartReference partRef) {
416 // Nothing to do
419 private void partUpdate(IWorkbenchPartReference partRef) {
420 if (partRef == null) {
421 return;
423 IWorkbenchPage page = partRef.getPage();
424 pageChanged(page);
427 private void runUpdate(final Shell shell) {
428 if (shell == null || shell.isDisposed()) {
429 return;
431 Display display = shell.getDisplay();
432 if (display == null || display.isDisposed()) {
433 return;
435 try {
436 display.asyncExec(new Runnable() {
438 @Override
439 public void run() {
440 if (!shell.isDisposed()) {
441 installDropTarget(shell);
445 } catch (RuntimeException ex) {
446 // Swallow