blob: 099c07800f09159d8a59cbaa379fe32f209c696d [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2008, 2021 IBM Corporation and others.
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License 2.0 which is available at
# https://www.eclipse.org/legal/epl-2.0.
#
# SPDX-License-Identifier: EPL-2.0
#
# Contributors:
# IBM Corporation - org.eclipse.jdt: initial API and implementation
# Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
#=============================================================================*/
package org.eclipse.statet.ecommons.ui.viewers.breadcrumb;
import org.eclipse.swt.SWT;
import org.eclipse.swt.accessibility.AccessibleAdapter;
import org.eclipse.swt.accessibility.AccessibleEvent;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.events.MenuDetectEvent;
import org.eclipse.swt.events.MenuDetectListener;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.events.TraverseEvent;
import org.eclipse.swt.events.TraverseListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Image;
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.Label;
import org.eclipse.swt.widgets.Shell;
/**
* The label and icon part of the breadcrumb item.
*/
class BreadcrumbItemDetails {
private final Composite detailComposite;
private final BreadcrumbItem parent;
private final Composite imageComposite;
private final Label elementImage;
private final Composite textComposite;
private final Label elementText;
private boolean textVisible;
private boolean selected;
private boolean hasFocus;
public BreadcrumbItemDetails(final BreadcrumbItem parent, final Composite parentContainer) {
this.parent= parent;
this.textVisible= true;
this.detailComposite= new Composite(parentContainer, SWT.NONE);
this.detailComposite.setLayoutData(new GridData(SWT.BEGINNING, SWT.CENTER, false, false));
GridLayout layout= new GridLayout(2, false);
layout.marginHeight= 0;
layout.marginWidth= 0;
layout.horizontalSpacing= 0;
this.detailComposite.setLayout(layout);
addElementListener(this.detailComposite);
this.imageComposite= new Composite(this.detailComposite, SWT.NONE);
this.imageComposite.setLayoutData(new GridData(SWT.BEGINNING, SWT.CENTER, false, false));
layout= new GridLayout(1, false);
layout.marginHeight= 1;
layout.marginWidth= 2;
this.imageComposite.setLayout(layout);
this.imageComposite.addPaintListener(new PaintListener() {
@Override
public void paintControl(final PaintEvent e) {
if (BreadcrumbItemDetails.this.hasFocus && !isTextVisible()) {
e.gc.drawFocus(e.x, e.y, e.width, e.height);
}
}
});
installFocusComposite(this.imageComposite);
addElementListener(this.imageComposite);
this.elementImage= new Label(this.imageComposite, SWT.NONE);
GridData layoutData= new GridData(SWT.BEGINNING, SWT.CENTER, false, false);
this.elementImage.setLayoutData(layoutData);
addElementListener(this.elementImage);
this.textComposite= new Composite(this.detailComposite, SWT.NONE);
this.textComposite.setLayoutData(new GridData(SWT.BEGINNING, SWT.CENTER, false, false));
layout= new GridLayout(1, false);
layout.marginHeight= 2;
layout.marginWidth= 2;
this.textComposite.setLayout(layout);
addElementListener(this.textComposite);
this.textComposite.addPaintListener(new PaintListener() {
@Override
public void paintControl(final PaintEvent e) {
if (BreadcrumbItemDetails.this.hasFocus && isTextVisible()) {
e.gc.drawFocus(e.x, e.y, e.width, e.height);
}
}
});
installFocusComposite(this.textComposite);
addElementListener(this.textComposite);
this.elementText= new Label(this.textComposite, SWT.NONE);
layoutData= new GridData(SWT.BEGINNING, SWT.CENTER, false, false);
this.elementText.setLayoutData(layoutData);
addElementListener(this.elementText);
this.textComposite.getAccessible().addAccessibleListener(new AccessibleAdapter() {
@Override
public void getName(final AccessibleEvent e) {
e.result= BreadcrumbItemDetails.this.elementText.getText();
}
});
this.imageComposite.getAccessible().addAccessibleListener(new AccessibleAdapter() {
@Override
public void getName(final AccessibleEvent e) {
e.result= BreadcrumbItemDetails.this.elementText.getText();
}
});
this.detailComposite.setTabList(new Control[] { this.textComposite });
}
/**
* Returns whether this element has the keyboard focus.
*
* @return true if this element has the keyboard focus.
*/
public boolean hasFocus() {
return this.hasFocus;
}
/**
* Sets the tool tip to the given text.
*
* @param text the tool tip
*/
public void setToolTip(final String text) {
if (isTextVisible()) {
this.elementText.getParent().setToolTipText(text);
this.elementText.setToolTipText(text);
this.elementImage.setToolTipText(text);
} else {
this.elementText.getParent().setToolTipText(null);
this.elementText.setToolTipText(null);
this.elementImage.setToolTipText(text);
}
}
/**
* Sets the image to the given image.
*
* @param image the image to use
*/
public void setImage(final Image image) {
if (image != this.elementImage.getImage()) {
this.elementImage.setImage(image);
}
}
/**
* Sets the text to the given text.
*
* @param text the text to use
*/
public void setText(String text) {
if (text == null) {
text= ""; //$NON-NLS-1$
}
if (!text.equals(this.elementText.getText())) {
this.elementText.setText(text);
}
}
/**
* Returns the width of this element.
*
* @return current width of this element
*/
public int getWidth() {
int result= 2;
if (this.elementImage.getImage() != null) {
result+= this.elementImage.computeSize(SWT.DEFAULT, SWT.DEFAULT).x;
}
if (this.textVisible && this.elementText.getText().length() > 0) {
result+= this.elementText.computeSize(SWT.DEFAULT, SWT.DEFAULT).x;
}
return result;
}
public void setTextVisible(final boolean enabled) {
if (this.textVisible == enabled) {
return;
}
this.textVisible= enabled;
final GridData data= (GridData) this.textComposite.getLayoutData();
data.exclude= !enabled;
this.textComposite.setVisible(enabled);
if (this.textVisible) {
this.detailComposite.setTabList(new Control[] { this.textComposite });
} else {
this.detailComposite.setTabList(new Control[] { this.imageComposite });
}
if (this.hasFocus) {
if (isTextVisible()) {
this.textComposite.setFocus();
} else {
this.imageComposite.setFocus();
}
}
updateSelection();
}
/**
* Tells whether this item shows a text or only an image.
*
* @return <code>true</code> if it shows a text and an image, false if it only shows the image
*/
public boolean isTextVisible() {
return this.textVisible;
}
/**
* Sets whether details should be shown.
*
* @param visible <code>true</code> if details should be shown
*/
public void setVisible(final boolean visible) {
this.detailComposite.setVisible(visible);
final GridData data= (GridData) this.detailComposite.getLayoutData();
data.exclude= !visible;
}
public void setSelected(final boolean selected) {
if (selected == this.selected) {
return;
}
this.selected= selected;
if (!this.selected) {
this.hasFocus= false;
}
updateSelection();
}
public void setFocus(final boolean enabled) {
if (enabled == this.hasFocus) {
return;
}
this.hasFocus= enabled;
if (this.hasFocus) {
if (isTextVisible()) {
this.textComposite.setFocus();
} else {
this.imageComposite.setFocus();
}
}
updateSelection();
}
private void updateSelection() {
Color background;
Color foreground;
if (this.selected && this.hasFocus) {
background= Display.getDefault().getSystemColor(SWT.COLOR_LIST_SELECTION);
foreground= Display.getDefault().getSystemColor(SWT.COLOR_LIST_SELECTION_TEXT);
} else {
foreground= null;
background= null;
}
if (isTextVisible()) {
this.textComposite.setBackground(background);
this.elementText.setBackground(background);
this.elementText.setForeground(foreground);
this.imageComposite.setBackground(null);
this.elementImage.setBackground(null);
} else {
this.imageComposite.setBackground(background);
this.elementImage.setBackground(background);
this.textComposite.setBackground(null);
this.elementText.setBackground(null);
this.elementText.setForeground(null);
}
this.textComposite.redraw();
this.imageComposite.redraw();
}
/**
* Install focus and key listeners to the given composite.
*
* @param composite the composite which may get focus
*/
private void installFocusComposite(final Composite composite) {
composite.addTraverseListener(new TraverseListener() {
@Override
public void keyTraversed(final TraverseEvent e) {
if (e.detail == SWT.TRAVERSE_TAB_NEXT || e.detail == SWT.TRAVERSE_TAB_PREVIOUS) {
int index= BreadcrumbItemDetails.this.parent.getViewer().getIndexOfItem(BreadcrumbItemDetails.this.parent);
if (e.detail == SWT.TRAVERSE_TAB_NEXT) {
index++;
} else {
index--;
}
if (index > 0 && index < BreadcrumbItemDetails.this.parent.getViewer().getItemCount()) {
BreadcrumbItemDetails.this.parent.getViewer().selectItem(BreadcrumbItemDetails.this.parent.getViewer().getItem(index));
}
e.doit= true;
}
}
});
composite.addKeyListener(new KeyListener() {
@Override
public void keyPressed(final KeyEvent e) {
final BreadcrumbViewer viewer= BreadcrumbItemDetails.this.parent.getViewer();
switch (e.keyCode) {
case SWT.ARROW_LEFT:
if (BreadcrumbItemDetails.this.selected) {
viewer.doTraverse(false);
e.doit= false;
} else {
viewer.selectItem(BreadcrumbItemDetails.this.parent);
}
break;
case SWT.ARROW_RIGHT:
if (BreadcrumbItemDetails.this.selected) {
viewer.doTraverse(true);
e.doit= false;
} else {
viewer.selectItem(BreadcrumbItemDetails.this.parent);
}
break;
case SWT.ARROW_DOWN:
if (!BreadcrumbItemDetails.this.selected) {
viewer.selectItem(BreadcrumbItemDetails.this.parent);
}
openDropDown();
e.doit= false;
break;
case SWT.KEYPAD_ADD:
if (!BreadcrumbItemDetails.this.selected) {
viewer.selectItem(BreadcrumbItemDetails.this.parent);
}
openDropDown();
e.doit= false;
break;
case SWT.CR:
if (!BreadcrumbItemDetails.this.selected) {
viewer.selectItem(BreadcrumbItemDetails.this.parent);
}
viewer.fireOpen();
break;
default:
if (e.character == ' ') {
if (!BreadcrumbItemDetails.this.selected) {
viewer.selectItem(BreadcrumbItemDetails.this.parent);
}
openDropDown();
e.doit= false;
}
break;
}
}
private void openDropDown() {
final BreadcrumbViewer viewer= BreadcrumbItemDetails.this.parent.getViewer();
final int index= viewer.getIndexOfItem(BreadcrumbItemDetails.this.parent);
final BreadcrumbItem parent= BreadcrumbItemDetails.this.parent.getViewer().getItem(index - 1);
Shell shell= parent.getDropDownShell();
if (shell == null) {
parent.openDropDownMenu();
shell= parent.getDropDownShell();
}
shell.setFocus();
}
@Override
public void keyReleased(final KeyEvent e) {
}
});
composite.addFocusListener(new FocusListener() {
@Override
public void focusGained(final FocusEvent e) {
if (!BreadcrumbItemDetails.this.hasFocus) {
BreadcrumbItemDetails.this.hasFocus= true;
updateSelection();
}
}
@Override
public void focusLost(final FocusEvent e) {
if (BreadcrumbItemDetails.this.hasFocus) {
BreadcrumbItemDetails.this.hasFocus= false;
updateSelection();
}
}
});
}
/**
* Add mouse listeners to the given control.
*
* @param control the control to which may be clicked
*/
private void addElementListener(final Control control) {
control.addMouseListener(new MouseListener() {
@Override
public void mouseDoubleClick(final MouseEvent e) {
}
@Override
public void mouseDown(final MouseEvent e) {
final BreadcrumbViewer viewer= BreadcrumbItemDetails.this.parent.getViewer();
final int parentIndex= viewer.getIndexOfItem(BreadcrumbItemDetails.this.parent) - 1;
Shell shell= null;
if (parentIndex >= 0) {//sanity check, must always hold
final BreadcrumbItem dropDownItem= viewer.getItem(parentIndex);
shell= dropDownItem.getDropDownShell();
}
viewer.selectItem(BreadcrumbItemDetails.this.parent);
if (shell == null && e.button == 1 && e.stateMask == 0) {
BreadcrumbItemDetails.this.parent.getViewer().fireDoubleClick();
}
}
@Override
public void mouseUp(final MouseEvent e) {
}
});
control.addMenuDetectListener(new MenuDetectListener() {
@Override
public void menuDetected(final MenuDetectEvent e) {
final BreadcrumbViewer viewer= BreadcrumbItemDetails.this.parent.getViewer();
viewer.selectItem(BreadcrumbItemDetails.this.parent);
BreadcrumbItemDetails.this.parent.getViewer().fireMenuDetect(e);
}
});
}
}