| /*=============================================================================# |
| # Copyright (c) 2012, 2020 Stephan Wahlbrink 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, or the Apache License, Version 2.0 |
| # which is available at https://www.apache.org/licenses/LICENSE-2.0. |
| # |
| # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 |
| # |
| # Contributors: |
| # Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation |
| #=============================================================================*/ |
| |
| package org.eclipse.statet.ecommons.emf.ui.forms; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.concurrent.atomic.AtomicReference; |
| |
| import org.eclipse.core.databinding.observable.value.IObservableValue; |
| import org.eclipse.core.databinding.observable.value.IValueChangeListener; |
| import org.eclipse.core.databinding.observable.value.ValueChangeEvent; |
| import org.eclipse.core.databinding.observable.value.WritableValue; |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.custom.StackLayout; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Control; |
| import org.eclipse.swt.widgets.Label; |
| |
| import org.eclipse.statet.ecommons.databinding.core.DataBindingSubContext; |
| import org.eclipse.statet.ecommons.emf.core.databinding.IEMFEditContext; |
| import org.eclipse.statet.ecommons.emf.ui.databinding.DetailContext; |
| |
| |
| public class DetailStack extends Composite { |
| |
| |
| private static class EmptyDetail extends Detail { |
| |
| public EmptyDetail(final DetailStack parent) { |
| super(parent); |
| |
| createContent(); |
| } |
| |
| @Override |
| protected Composite createComposite(final DetailStack parent) { |
| return new Composite(parent, SWT.NONE); |
| } |
| |
| @Override |
| protected void createContent(final Composite composite) { |
| } |
| |
| } |
| |
| private class DetailEntry { |
| |
| private final Object key; |
| private final Detail detail; |
| |
| private IEMFEditContext context; |
| private DataBindingSubContext dbc; |
| |
| public DetailEntry(final Object key, final Detail detail) { |
| this.key= key; |
| this.detail= detail; |
| } |
| |
| } |
| |
| |
| private final IEFFormPage page; |
| |
| private final StackLayout layout; |
| |
| private final List<DetailEntry> details; |
| |
| private final int limit= 10; |
| |
| private IEMFEditContext context; |
| |
| private DetailEntry currentDetail; |
| private EObject currentValue; |
| |
| |
| public DetailStack(final IEFFormPage page, final Composite parent) { |
| super(parent, SWT.NONE); |
| |
| this.page= page; |
| |
| this.layout= new StackLayout(); |
| setLayout(this.layout); |
| |
| this.details= new ArrayList<>(this.limit + 1); |
| } |
| |
| |
| public IEFFormPage getPage() { |
| return this.page; |
| } |
| |
| protected void dispose(final DetailEntry entry) { |
| { final Composite composite= entry.detail.getComposite(); |
| if (composite != null && !composite.isDisposed()) { |
| composite.dispose(); |
| } |
| } |
| if (entry.dbc != null) { |
| entry.dbc.dispose(); |
| entry.dbc= null; |
| } |
| } |
| |
| public void showDetail(final EObject value) { |
| int index= 0; |
| final Object key= getKey(value); |
| DetailEntry entry= null; |
| for (; index < this.details.size(); index++) { |
| final DetailEntry detail= this.details.get(index); |
| if (key == detail.key) { |
| entry= detail; |
| break; |
| } |
| } |
| if (entry == null) { |
| final Detail detail= createDetail(value); |
| entry= new DetailEntry(key, detail); |
| this.details.add(0, entry); |
| initDetailBindings(entry, value); |
| } |
| else { |
| if (entry.dbc == null) { |
| initDetailBindings(entry, value); |
| } |
| if (index != 0) { |
| this.details.remove(index); |
| this.details.add(0, entry); |
| } |
| } |
| |
| this.currentDetail= this.details.get(0); |
| this.currentValue= value; |
| this.layout.topControl= this.currentDetail.detail.getComposite(); |
| layout(); |
| getParent().layout(new Control[] { this }); |
| getPage().reflow(true); |
| |
| if (this.details.size() > this.limit) { |
| dispose(this.details.remove(this.details.size() - 1)); |
| } |
| } |
| |
| protected Object getKey(final EObject value) { |
| return value; |
| } |
| |
| |
| protected Detail createDetail(final EObject value) { |
| return new EmptyDetail(this); |
| } |
| |
| protected void createDetailContent(final Composite composite, final EObject value) { |
| if (value == null) { |
| return; |
| } |
| final Label label= new Label(composite, SWT.NONE); |
| label.setText(value.toString()); |
| } |
| |
| public void setContext(final IEMFEditContext context) { |
| this.context= context; |
| |
| final IObservableValue baseValue= context.getBaseObservable(); |
| baseValue.addValueChangeListener(new IValueChangeListener() { |
| @Override |
| public void handleValueChange(final ValueChangeEvent event) { |
| showDetail((EObject) event.diff.getNewValue()); |
| } |
| }); |
| showDetail((EObject) baseValue.getValue()); |
| } |
| |
| protected IEMFEditContext createDetailContext(final IEMFEditContext parent, |
| final IObservableValue detailValue) { |
| return new DetailContext(parent, detailValue); |
| } |
| |
| private void initDetailBindings(final DetailEntry entry, final EObject value) { |
| final IEMFEditContext context= this.context; |
| if (context == null || entry.dbc != null) { |
| if (entry.context != null) { |
| entry.context.getBaseObservable().setValue(value); |
| } |
| return; |
| } |
| final AtomicReference<RuntimeException> error= new AtomicReference<>(); |
| |
| entry.dbc= new DataBindingSubContext(context.getDataBindingContext()); |
| entry.dbc.run(new Runnable() { |
| @Override |
| public void run() { |
| try { |
| final IObservableValue contextValue= new WritableValue(context.getRealm(), |
| value, EObject.class ); |
| entry.context= createDetailContext(context, contextValue); |
| entry.detail.addBindings(entry.context); |
| } |
| catch (final RuntimeException e) { |
| error.set(e); |
| } |
| } |
| }); |
| |
| if (error.get() != null) { |
| throw error.get(); |
| } |
| } |
| |
| // @Override |
| // public Point computeSize(int wHint, int hHint, boolean changed) { |
| // Point size= super.computeSize(wHint, hHint, changed); |
| // System.out.println("" + wHint + ", " + hHint + " -> " + size); |
| // return size; |
| // } |
| |
| } |