| /*=============================================================================# |
| # 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); |
| } |
| |
| }); |
| } |
| |
| } |