InternalPropertySectionUtil.java
/**
* Copyright (c) 2004-2025 Carnegie Mellon University and others. (see Contributors file).
* All Rights Reserved.
*
* NO WARRANTY. ALL MATERIAL IS FURNISHED ON AN "AS-IS" BASIS. CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY
* KIND, EITHER EXPRESSED OR IMPLIED, AS TO ANY MATTER INCLUDING, BUT NOT LIMITED TO, WARRANTY OF FITNESS FOR PURPOSE
* OR MERCHANTABILITY, EXCLUSIVITY, OR RESULTS OBTAINED FROM USE OF THE MATERIAL. CARNEGIE MELLON UNIVERSITY DOES NOT
* MAKE ANY WARRANTY OF ANY KIND WITH RESPECT TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT.
*
* 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
*
* Created, in part, with funding and support from the United States Government. (see Acknowledgments file).
*
* This program includes and/or can make use of certain third party source code, object code, documentation and other
* files ("Third Party Software"). The Third Party Software that is used by this program is dependent upon your system
* configuration. By using this program, You agree to comply with any and all relevant Third Party Software terms and
* conditions contained in any such Third Party Software or separate license file distributed with such Third Party
* Software. The parties who own the Third Party Software ("Third Party Licensors") are intended third party benefici-
* aries to this license with respect to the terms applicable to their Third Party Software. Third Party Software li-
* censes only apply to the Third Party Software and not any other portion of this program or this program as a whole.
*/
package org.osate.ge.internal.ui.util;
import org.eclipse.jface.layout.RowLayoutFactory;
import org.eclipse.jface.util.LocalSelectionTransfer;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.CellLabelProvider;
import org.eclipse.jface.viewers.ComboViewer;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TableViewerColumn;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DragSourceAdapter;
import org.eclipse.swt.dnd.DragSourceEvent;
import org.eclipse.swt.dnd.DropTargetAdapter;
import org.eclipse.swt.dnd.DropTargetEvent;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.ui.views.properties.tabbed.ITabbedPropertyConstants;
import org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetWidgetFactory;
/**
* Utility class containing members used to implement property section
*
*/
public final class InternalPropertySectionUtil {
/**
* Private constructor to prevent instantiation.
*/
private InternalPropertySectionUtil() {
}
/**
* Creates a combo viewer
* @param container the container for the viewer
* @param lblWidth the width of the label
* @param selectionListener the selection listener for the combo viewer
* @param lblProvider the label provider for the combo viewer
* @return the new combo viewer
*/
public static ComboViewer createComboViewer(final Composite container, final int lblWidth,
final SelectionListener selectionListener, final LabelProvider lblProvider) {
final ComboViewer comboViewer = new ComboViewer(container);
comboViewer.setContentProvider(new ArrayContentProvider());
comboViewer.setLabelProvider(lblProvider);
final Combo combo = comboViewer.getCombo();
final FormData fd = new FormData();
fd.left = new FormAttachment(0, lblWidth);
combo.setLayoutData(fd);
combo.addSelectionListener(selectionListener);
// Disable scroll so selection listeners are not constantly called resulting in a burst of AADL modifications.
combo.addListener(SWT.MouseVerticalWheel, event -> event.doit = false);
return comboViewer;
}
/**
* Sets the context help ID for the specified control to be that of the properties view
* @param control the control for which to set the context help ID
* @see ContextHelpUtil#PROPERTIES_VIEW
*/
public static void setPropertiesHelp(final Control control) {
ContextHelpUtil.setHelp(control, ContextHelpUtil.PROPERTIES_VIEW);
}
/**
* Creates a button
* @param widgetFactory the widget factory used to create the button
* @param composite the composite to which the button will be added
* @param data the data from the button
* @param listener the selection listener for the button
* @param txt the text for the button
* @param style the style for the button
* @return the new button
*/
public static Button createButton(final TabbedPropertySheetWidgetFactory widgetFactory, final Composite composite,
final Object data, final SelectionListener listener, final String txt, final int style) {
final Button btn = widgetFactory.createButton(composite, txt, style);
btn.setData(data);
btn.addSelectionListener(listener);
return btn;
}
/**
* Creates a composite with a row layout
* @param widgetFactory the factory to use to create the composite
* @param parent the component to which the new composite will be added.
* @param offset the horizontal offset for the composite. This typically matches the label width of other widgets
* @return the new composite
*/
public static Composite createRowLayoutComposite(final TabbedPropertySheetWidgetFactory widgetFactory,
final Composite parent, final int offset) {
final Composite container = widgetFactory.createComposite(parent);
container.setLayout(RowLayoutFactory.fillDefaults().wrap(false).create());
final FormData ld = new FormData();
ld.left = new FormAttachment(0, offset);
ld.top = new FormAttachment(0, ITabbedPropertyConstants.VSPACE);
container.setLayoutData(ld);
return container;
}
/**
* Creates a new table viewer column
* @param tableViewer the table viewer for which the column is being created
* @param colHeader the header of the column
* @param style the style of the table viewer column
* @param cellLabelProvider the label provider which provides the text for the cells
* @return the new table viewer column
*/
public static TableViewerColumn createTableColumnViewer(final TableViewer tableViewer, final String colHeader,
final int style, final CellLabelProvider cellLabelProvider) {
final TableViewerColumn tableViewerColumn = new TableViewerColumn(tableViewer, style);
tableViewerColumn.getColumn().setText(colHeader);
tableViewerColumn.setLabelProvider(cellLabelProvider);
return tableViewerColumn;
}
/**
* Helper class for implementing drag and drop for a table.
*
*/
public static class DragAndDropSupport {
private final int operations = DND.DROP_MOVE;
private final Transfer[] types = new Transfer[] { LocalSelectionTransfer.getTransfer() };
private final Table table;
private final ExecuteOrderChange execute;
private DragAndDropElement dragElement; // Element moving indices
private DragAndDropElement dropElement; // Element dropped upon
/**
* Creates a new instance for use with a table
* @param table the table whose items uses {@link DragAndDropElement} instances for its data
* @param execute the function to use to modify the order
*/
public DragAndDropSupport(final Table table,
final ExecuteOrderChange execute) {
this.table = table;
this.execute = execute;
}
/**
* Drag source adapter which updates {@link #dragElement} when a drag starts
* @see TableViewer#addDropSupport(int, Transfer[], org.eclipse.swt.dnd.DropTargetListener)
*/
public final DragSourceAdapter dragSourceListener = new DragSourceAdapter() {
@Override
public void dragStart(final DragSourceEvent event) {
dragElement = (DragAndDropElement) table.getSelection()[0].getData();
}
};
/**
* Drop target adapter for handling dropping
* Drag element will be placed above targeted element
* @see TableViewer#addDropSupport(int, Transfer[], org.eclipse.swt.dnd.DropTargetListener)
*/
public final DropTargetAdapter dropTargetListener = new DropTargetAdapter() {
@Override
public void drop(final DropTargetEvent event) {
final int curIndex = dragElement.getIndex() - 1;
final int newIndex = getNewIndex(dropElement, curIndex);
execute.apply(newIndex, curIndex, dragElement);
dragElement = null;
dropElement = null;
}
private int getNewIndex(final DragAndDropElement targetElement, final int dragIndex) {
// Set in last index
if (targetElement == null) {
return table.getItemCount() - 1;
}
final int targetIndex = targetElement.getIndex() - 1;
// If dragging up, subtract 1
return dragIndex < targetIndex ? targetIndex - 1 : targetIndex;
}
@Override
public void dragOver(final DropTargetEvent event) {
final TableItem tableItem;
final DragAndDropElement dragTargetItem;
if (event.item instanceof TableItem) {
tableItem = (TableItem) event.item;
dragTargetItem = (DragAndDropElement) tableItem.getData();
} else {
tableItem = null;
dragTargetItem = null;
}
if (dragTargetItem != dropElement) {
if (dragTargetItem != null) {
// Scroll while dragging
if (dragTargetItem.getIndex() > 1) {
table.showItem(table.getItem(dragTargetItem.getIndex() - 2));
}
if (dragTargetItem.getIndex() < table.getItemCount()) {
table.showItem(table.getItem(dragTargetItem.getIndex()));
}
}
// Cleans up lines drawn for previous targeted index
table.redraw();
}
// Set next target element
dropElement = dragTargetItem;
// Draw the drop index line
final GC gc = new GC(table);
gc.setLineWidth(3);
final Rectangle bounds = getBounds(tableItem);
gc.drawLine(bounds.x, bounds.height, bounds.width, bounds.height);
gc.dispose();
}
// Bounds used for drawing target index line
private Rectangle getBounds(final TableItem targetItem) {
final Rectangle bounds;
final int y;
// Draw below last item for last index
if (targetItem == null) {
bounds = table.getItem(table.getItemCount() - 1).getBounds();
y = bounds.y + bounds.height;
} else {
bounds = targetItem.getBounds();
y = bounds.y + 1;
}
return new Rectangle(bounds.x - 5, y, bounds.x + table.getBounds().width, y);
}
@Override
public void dragLeave(final DropTargetEvent event) {
// Cleans up any leftover lines
table.redraw();
}
};
/**
* Returns supported drag and drop operations
* @return supported drag and drop operations
*/
public int getDragAndDropOperations() {
return operations;
}
/**
* Returns the supported transfer types
* @return the supported transfer types
*/
public Transfer[] getTransferTypes() {
return types;
}
}
/**
* The data type for items supported by {@link DragAndDropSupport}
* @see TableItem#setData(Object)
*/
public static class DragAndDropElement {
private final String name;
private final int index;
/**
* Creates a new instance
* @param name the name of the item. Ignored by {@link DragAndDropSupport}
* @param index the index of the item
*/
public DragAndDropElement(final String name, final int index) {
this.name = name;
this.index = index;
}
/**
* Returns the name of the item
* @return the name of the item
*/
public String getName() {
return name;
}
/**
* Returns the index of the item
* @return the index of the item
*/
public int getIndex() {
return index;
}
}
/**
* Selection listener used to increase or decrease and item's index.
* The listener must be attached to a button which has a boolean data value.
* If the value is true then the item is moved "up" by decreasing its index
* If the value is false then the item is moved "down" by increasing its index.
*
* @see Button#setData(Object)
*/
public static class UpDownButtonSelectionAdapter extends SelectionAdapter {
private final TableViewer tableViewer;
private final ExecuteOrderChange executeChangeOrder;
/**
* Creates a new instance which moves the item selected in the specified table viewer
* @param tableViewer the table viewer containing the items adjusted by this listener.
* @param executeChangeOrder the object to use to change the index of an element
*/
public UpDownButtonSelectionAdapter(final TableViewer tableViewer,
final ExecuteOrderChange executeChangeOrder) {
this.tableViewer = tableViewer;
this.executeChangeOrder = executeChangeOrder;
}
@Override
public void widgetSelected(final SelectionEvent e) {
if (!tableViewer.getStructuredSelection().isEmpty()) {
final DragAndDropElement dNDElement = (DragAndDropElement) tableViewer.getStructuredSelection()
.getFirstElement();
final boolean direction = (boolean) ((Button) e.widget).getData();
final int targetIndex = dNDElement.getIndex() + (direction ? -1 : 1);
final int newIndex = getNewIndex(tableViewer.getTable().getItemCount(), targetIndex);
executeChangeOrder.apply(newIndex, dNDElement.getIndex() - 1, dNDElement);
}
}
private static int getNewIndex(final int totalSize, final int newIndex) {
if (newIndex < 1) {
return 0;
}
return (newIndex > totalSize ? totalSize : newIndex) - 1;
}
}
/**
* Functional interface for reordering elements
*/
public interface ExecuteOrderChange {
/**
* Changes the index of the specified item
* @param newIndex the new index of the item
* @param curIndex the current index of the item
* @param element the item to reorder
*/
public void apply(final Integer newIndex, final Integer curIndex, final DragAndDropElement element);
}
}