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
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
;
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
;
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
);
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
);
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
);
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
) {
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
;
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
{
183 public void dragEnter(DropTargetEvent e
) {
184 updateDragDetails(e
);
188 public void dragOver(DropTargetEvent e
) {
189 updateDragDetails(e
);
193 public void dragLeave(DropTargetEvent e
) {
194 if (e
.detail
== DND
.DROP_NONE
) {
200 public void dropAccept(DropTargetEvent e
) {
201 updateDragDetails(e
);
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) {
217 e
.detail
= allowedOperations
;
220 private void updateDragDetails(DropTargetEvent e
) {
221 if (dropTargetIsValid(e
, false)) {
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,
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
237 final String url
= getUrl(e
.data
);
238 if (!GitUrlChecker
.isValidGitUrl(url
)) {
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) {
261 public void drop(DropTargetEvent event
) {
262 if (!URLTransfer
.getInstance()
263 .isSupportedType(event
.currentDataType
)) {
267 if (event
.data
== null) {
269 event
.detail
= DND
.DROP_NONE
;
272 if (!dropTargetIsValid(event
, true)) {
274 event
.detail
= DND
.DROP_NONE
;
277 final String url
= getUrl(event
.data
);
278 DropTarget source
= (DropTarget
) event
.getSource();
279 Display display
= source
.getDisplay();
280 display
.asyncExec(new Runnable() {
289 private String
getUrl(Object eventData
) {
290 if (!(eventData
instanceof String
)) {
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];
302 private class WorkbenchListener
implements IPartListener2
, IPageListener
,
303 IPerspectiveListener
, IWindowListener
{
306 public void perspectiveActivated(IWorkbenchPage page
,
307 IPerspectiveDescriptor perspective
) {
312 public void perspectiveChanged(IWorkbenchPage page
,
313 IPerspectiveDescriptor perspective
, String changeId
) {
318 public void pageActivated(IWorkbenchPage page
) {
323 public void pageClosed(IWorkbenchPage page
) {
328 public void pageOpened(IWorkbenchPage page
) {
332 private void pageChanged(IWorkbenchPage page
) {
336 IWorkbenchWindow workbenchWindow
= page
.getWorkbenchWindow();
337 windowChanged(workbenchWindow
);
341 public void windowActivated(IWorkbenchWindow window
) {
342 windowChanged(window
);
345 private void windowChanged(IWorkbenchWindow window
) {
346 if (window
== null) {
349 Shell shell
= window
.getShell();
354 public void windowDeactivated(IWorkbenchWindow window
) {
359 public void windowClosed(IWorkbenchWindow window
) {
364 public void windowOpened(IWorkbenchWindow window
) {
368 public void hookWindow(IWorkbenchWindow window
) {
369 if (window
== null) {
372 window
.addPageListener(this);
373 window
.addPerspectiveListener(this);
374 IPartService partService
= window
.getService(IPartService
.class);
375 partService
.addPartListener(this);
376 windowChanged(window
);
380 public void partOpened(IWorkbenchPartReference partRef
) {
385 public void partActivated(IWorkbenchPartReference partRef
) {
390 public void partBroughtToTop(IWorkbenchPartReference partRef
) {
395 public void partVisible(IWorkbenchPartReference partRef
) {
400 public void partClosed(IWorkbenchPartReference partRef
) {
405 public void partDeactivated(IWorkbenchPartReference partRef
) {
410 public void partHidden(IWorkbenchPartReference partRef
) {
415 public void partInputChanged(IWorkbenchPartReference partRef
) {
419 private void partUpdate(IWorkbenchPartReference partRef
) {
420 if (partRef
== null) {
423 IWorkbenchPage page
= partRef
.getPage();
427 private void runUpdate(final Shell shell
) {
428 if (shell
== null || shell
.isDisposed()) {
431 Display display
= shell
.getDisplay();
432 if (display
== null || display
.isDisposed()) {
436 display
.asyncExec(new Runnable() {
440 if (!shell
.isDisposed()) {
441 installDropTarget(shell
);
445 } catch (RuntimeException ex
) {