Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 07a0a3b7c9dc55cdedd0359d63635690147886db (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
/*******************************************************************************
 * Copyright (c) 2008, 2009 IBM Corporation and others.
 *
 * 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:
 *     IBM Corporation - initial API and implementation
 *     Pawel Piech (Wind River) - adapted breadcrumb for use in Debug view (Bug 252677)
 *******************************************************************************/
package org.eclipse.debug.internal.ui.viewers.breadcrumb;

import org.eclipse.core.runtime.Assert;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.IOpenListener;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.OpenEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Widget;


/**
 * Breadcrumb base class.  It creates the breadcrumb viewer and manages
 * its activation.
 * <p>
 * Clients must implement the abstract methods.
 * </p>
 *
 * @since 3.5
 */
public abstract class AbstractBreadcrumb {

	private BreadcrumbViewer fBreadcrumbViewer;

	private boolean fHasFocus;

	private Composite fComposite;

	private Listener fDisplayFocusListener;
	private Listener fDisplayKeyListener;

	public AbstractBreadcrumb() {
	}

	/**
	 * The active element of the editor.
	 *
	 * @return the active element of the editor, or <b>null</b> if none
	 */
	protected abstract Object getCurrentInput();

	/**
	 * Create and configure the viewer used to display the parent chain.
	 *
	 * @param parent the parent composite
	 * @return the viewer
	 */
	protected abstract BreadcrumbViewer createViewer(Composite parent);

	/**
	 * Open the element in a new editor if possible.
	 *
	 * @param selection element the element to open
	 * @return true if the element could be opened
	 */
	protected abstract boolean open(ISelection selection);

	/**
	 * The breadcrumb has been activated. Implementors must retarget the editor actions to the
	 * breadcrumb aware actions.
	 */
	protected abstract void activateBreadcrumb();

	/**
	 * The breadcrumb has been deactivated. Implementors must retarget the breadcrumb actions to the
	 * editor actions.
	 */
	protected abstract void deactivateBreadcrumb();

    /**
     * Returns the selection provider for this breadcrumb.
     *
     * @return the selection provider for this breadcrumb
     */
	public ISelectionProvider getSelectionProvider() {
		return fBreadcrumbViewer;
	}

    /**
     * Set the input of the breadcrumb to the given element
     *
     * @param element the input element can be <code>null</code>
     */
	public void setInput(Object element) {
		if (element == null || fBreadcrumbViewer == null || fBreadcrumbViewer.getControl().isDisposed())
			return;

		Object input= fBreadcrumbViewer.getInput();
		if (input == element || element.equals(input)) {
		    refresh();
			return;
		}

		fBreadcrumbViewer.setInput(element);
	}

	protected void refresh() {
	    if (!fBreadcrumbViewer.getControl().isDisposed()) {
	        fBreadcrumbViewer.refresh();
	    }
	}

    /**
     * Activates the breadcrumb. This sets the keyboard focus
     * inside this breadcrumb and retargets the editor
     * actions.
     */
	public void activate() {
		if (fBreadcrumbViewer.getSelection().isEmpty())
			fBreadcrumbViewer.setSelection(new StructuredSelection(fBreadcrumbViewer.getInput()));
		fBreadcrumbViewer.setFocus();
	}

    /**
     * A breadcrumb is active if it either has the focus or another workbench part has the focus and
     * the breadcrumb had the focus before the other workbench part was made active.
     *
     * @return <code>true</code> if this breadcrumb is active
     */
	public boolean isActive() {
		return true;
	}

    /**
     * Create breadcrumb content.
     *
     * @param parent the parent of the content
     * @return the control containing the created content
     */
	public Control createContent(Composite parent) {
		Assert.isTrue(fComposite == null, "Content must only be created once."); //$NON-NLS-1$

		boolean rtl= (parent.getShell().getStyle() & SWT.RIGHT_TO_LEFT) != 0;
		//boolean rtl = true;

		fComposite= new Composite(parent, rtl ? SWT.RIGHT_TO_LEFT : SWT.NONE);
		GridData data= new GridData(SWT.FILL, SWT.TOP, true, false);
		fComposite.setLayoutData(data);
		GridLayout gridLayout= new GridLayout(1, false);
		gridLayout.marginWidth= 0;
		gridLayout.marginHeight= 0;
		gridLayout.verticalSpacing= 0;
		gridLayout.horizontalSpacing= 0;
		fComposite.setLayout(gridLayout);

		fDisplayFocusListener= new Listener() {
			@Override
			public void handleEvent(Event event) {
			    if (fComposite.isDisposed()) return;

				if (isBreadcrumbEvent(event)) {
					if (fHasFocus)
						return;

					focusGained();
				} else {
					if (!fHasFocus)
						return;

					focusLost();
				}
			}
		};
		Display.getCurrent().addFilter(SWT.FocusIn, fDisplayFocusListener);

		fBreadcrumbViewer= createViewer(fComposite);

		fBreadcrumbViewer.addDoubleClickListener(new IDoubleClickListener() {
			@Override
			public void doubleClick(DoubleClickEvent event) {
				Object element= ((IStructuredSelection) event.getSelection()).getFirstElement();
				if (element == null)
					return;

				BreadcrumbItem item= (BreadcrumbItem) fBreadcrumbViewer.doFindItem(element);
				if (item == null)
					return;
				item.openDropDownMenu();
			}
		});

		fBreadcrumbViewer.addOpenListener(new IOpenListener() {
			@Override
			public void open(OpenEvent event) {
				doOpen(event.getSelection());
			}
		});

		return fComposite;
	}

    /**
     * Dispose all resources hold by this breadcrumb.
     */
	public void dispose() {
		if (fDisplayFocusListener != null) {
			Display.getDefault().removeFilter(SWT.FocusIn, fDisplayFocusListener);
		}
		deinstallDisplayListeners();
	}

	/**
	 * Either reveal the selection in the editor or open the selection in a new editor. If both fail
	 * open the child pop up of the selected element.
	 *
	 * @param selection the selection to open
	 */
	private void doOpen(ISelection selection) {
		if (open(selection)) {
            fBreadcrumbViewer.setInput(getCurrentInput());
		}
	}

	/**
	 * Focus has been transfered into the breadcrumb.
	 */
	private void focusGained() {
		if (fHasFocus)
			focusLost();

		fHasFocus= true;

		installDisplayListeners();

		activateBreadcrumb();
	}

	/**
	 * Focus has been revoked from the breadcrumb.
	 */
	private void focusLost() {
		fHasFocus= false;

		deinstallDisplayListeners();

		deactivateBreadcrumb();
	}

	/**
	 * Installs all display listeners.
	 */
	private void installDisplayListeners() {
		//Sanity check
		deinstallDisplayListeners();

		fDisplayKeyListener= new Listener() {
			@Override
			public void handleEvent(Event event) {
				if (event.keyCode != SWT.ESC)
					return;

				if (!isBreadcrumbEvent(event))
					return;
			}
		};
		Display.getDefault().addFilter(SWT.KeyDown, fDisplayKeyListener);
	}

	/**
	 * Removes all previously installed display listeners.
	 */
	private void deinstallDisplayListeners() {
		if (fDisplayKeyListener != null) {
			Display.getDefault().removeFilter(SWT.KeyDown, fDisplayKeyListener);
			fDisplayKeyListener= null;
		}
	}

	/**
	 * Tells whether the given event was issued inside the breadcrumb viewer's control.
	 *
	 * @param event the event to inspect
	 * @return <code>true</code> if event was generated by a breadcrumb child
	 */
	private boolean isBreadcrumbEvent(Event event) {
		if (fBreadcrumbViewer == null)
			return false;

		Widget item= event.widget;
		if (!(item instanceof Control))
			return false;

		Shell dropDownShell= fBreadcrumbViewer.getDropDownShell();
		if (dropDownShell != null && isChild((Control) item, dropDownShell))
			return true;

		return isChild((Control) item, fBreadcrumbViewer.getControl());
	}

	private boolean isChild(Control child, Control parent) {
		if (child == null)
			return false;

		if (child == parent)
			return true;

		return isChild(child.getParent(), parent);
	}
}

Back to the top