| /** |
| * Copyright (c) 2011, 2014 - Lunifera GmbH (Gross Enzersdorf, Austria), Loetz GmbH&Co.KG (69115 Heidelberg, Germany) |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * Florian Pirchner - Initial implementation |
| */ |
| package org.eclipse.osbp.vaaclipse.addons.ecview.views; |
| |
| import java.util.Collections; |
| import java.util.Date; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Locale; |
| import java.util.Map; |
| import java.util.NoSuchElementException; |
| import java.util.Set; |
| |
| import javax.annotation.PostConstruct; |
| import javax.annotation.PreDestroy; |
| import javax.inject.Inject; |
| import javax.inject.Named; |
| |
| import org.eclipse.e4.core.contexts.IEclipseContext; |
| import org.eclipse.e4.ui.di.Persist; |
| import org.eclipse.e4.ui.model.application.MApplication; |
| import org.eclipse.e4.ui.model.application.commands.MCommand; |
| import org.eclipse.e4.ui.model.application.ui.basic.MPart; |
| import org.eclipse.e4.ui.model.application.ui.menu.MHandledToolItem; |
| import org.eclipse.e4.ui.model.application.ui.menu.MMenuFactory; |
| import org.eclipse.e4.ui.model.application.ui.menu.MToolBar; |
| import org.eclipse.e4.ui.model.application.ui.menu.MToolBarElement; |
| import org.eclipse.e4.ui.model.application.ui.menu.MToolItem; |
| import org.eclipse.e4.ui.workbench.modeling.EModelService; |
| import org.eclipse.e4.ui.workbench.modeling.EPartService; |
| import org.eclipse.emf.common.notify.impl.AdapterImpl; |
| import org.eclipse.osbp.dsl.dto.lib.services.DtoServiceException; |
| import org.eclipse.osbp.dsl.dto.lib.services.IDTOService; |
| import org.eclipse.osbp.ecview.core.common.beans.ISlot; |
| import org.eclipse.osbp.ecview.core.common.context.ContextException; |
| import org.eclipse.osbp.ecview.core.common.context.IViewContext; |
| import org.eclipse.osbp.ecview.core.common.editpart.IFieldEditpart; |
| import org.eclipse.osbp.ecview.core.common.model.core.CoreModelPackage; |
| import org.eclipse.osbp.ecview.core.common.model.core.YBeanSlot; |
| import org.eclipse.osbp.ecview.core.common.model.core.YEmbeddable; |
| import org.eclipse.osbp.ecview.core.common.model.core.YExposedAction; |
| import org.eclipse.osbp.ecview.core.common.model.core.YField; |
| import org.eclipse.osbp.ecview.core.common.model.core.YFocusable; |
| import org.eclipse.osbp.ecview.core.common.model.core.YView; |
| import org.eclipse.osbp.ecview.core.common.model.core.util.BindingIdUtil; |
| import org.eclipse.osbp.ecview.core.common.validation.IFieldValidationManager; |
| import org.eclipse.osbp.ecview.core.util.emf.ModelUtil; |
| import org.eclipse.osbp.runtime.common.annotations.DtoUtils; |
| import org.eclipse.osbp.runtime.common.event.IEventBroker; |
| import org.eclipse.osbp.runtime.common.i18n.II18nService; |
| import org.eclipse.osbp.runtime.common.state.ISharedStateContext; |
| import org.eclipse.osbp.runtime.common.state.SharedStateUnitOfWork; |
| import org.eclipse.osbp.runtime.common.validation.IStatus; |
| import org.eclipse.osbp.runtime.common.validation.ValidationKind; |
| import org.eclipse.osbp.runtime.web.ecview.presentation.vaadin.VaadinRenderer; |
| import org.eclipse.osbp.runtime.web.vaadin.common.resource.IResourceProvider; |
| import org.eclipse.osbp.runtime.web.vaadin.components.dialogs.AcceptDeleteDialog; |
| import org.eclipse.osbp.runtime.web.vaadin.components.dialogs.AcceptLoosingDataDialog; |
| import org.eclipse.osbp.runtime.web.vaadin.components.dialogs.AcceptReloadDialog; |
| import org.eclipse.osbp.vaaclipse.addons.common.api.IE4Constants; |
| import org.eclipse.osbp.vaaclipse.addons.common.api.IE4Topics; |
| import org.eclipse.osbp.vaaclipse.addons.common.api.di.Callback; |
| import org.eclipse.osbp.vaaclipse.addons.common.api.di.Delete; |
| import org.eclipse.osbp.vaaclipse.addons.common.api.di.EnabledState; |
| import org.eclipse.osbp.vaaclipse.addons.common.api.di.Load; |
| import org.eclipse.osbp.vaaclipse.addons.common.api.di.Validate; |
| import org.eclipse.osbp.vaaclipse.addons.common.api.status.IStatusManager; |
| import org.eclipse.osbp.vaaclipse.addons.common.api.status.IStatusScope; |
| import org.eclipse.osbp.vaaclipse.addons.common.event.EventTopicNormalizer; |
| import org.eclipse.osbp.vaaclipse.addons.ecview.event.E4EventBrokerAdapter; |
| import org.eclipse.osbp.vaaclipse.addons.keybinding.IKeyBindingService; |
| import org.eclipse.osbp.vaaclipse.publicapi.commands.IPartItemExecutionService; |
| import org.osgi.service.event.Event; |
| import org.osgi.service.event.EventHandler; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| import com.vaadin.event.LayoutEvents; |
| import com.vaadin.event.LayoutEvents.LayoutClickEvent; |
| import com.vaadin.ui.Notification; |
| import com.vaadin.ui.Notification.Type; |
| import com.vaadin.ui.VerticalLayout; |
| |
| /** |
| * A generic ECView view part implementation. It will be used by the |
| * DynamicViewSupport to create views on the fly. |
| * <p> |
| * Client should not subclass this implementation. |
| */ |
| public class GenericECViewPart implements IFieldValidationManager.Listener { |
| |
| /** The Constant BEAN_SLOT__VALIDATION_ERROR. */ |
| private static final String BEAN_SLOT__VALIDATION_ERROR = "validationError"; |
| |
| /** The Constant LOGGER. */ |
| private static final Logger LOGGER = LoggerFactory |
| .getLogger(GenericECViewPart.class); |
| |
| /** The eclipse context. */ |
| @Inject |
| private IEclipseContext eclipseContext; |
| |
| /** The m part. */ |
| @Inject |
| private MPart mPart; |
| |
| /** The parent layout. */ |
| @Inject |
| private VerticalLayout parentLayout; |
| |
| /** The e4 event broker. */ |
| @Inject |
| private org.eclipse.e4.core.services.events.IEventBroker e4EventBroker; |
| |
| /** The topic normalizer. */ |
| @Inject |
| private EventTopicNormalizer topicNormalizer; |
| |
| /** The i18n service. */ |
| @Inject |
| private II18nService i18nService; |
| |
| /** The resource provider. */ |
| @Inject |
| private IResourceProvider resourceProvider; |
| |
| /** The y view. */ |
| @Inject |
| private YView yView; |
| |
| /** The shared state. */ |
| @Inject |
| private ISharedStateContext sharedState; |
| |
| /** The dto service. */ |
| @Inject |
| private IDTOService<Object> dtoService; |
| |
| /** The part service. */ |
| @Inject |
| private EPartService partService; |
| |
| /** The status manager. */ |
| @Inject |
| private IStatusManager statusManager; |
| |
| /** The key binding service. */ |
| @Inject |
| private IKeyBindingService keyBindingService; |
| |
| /** The command info. */ |
| private Map<String, CommandInfo> commandInfo = new HashMap<String, CommandInfo>(); |
| |
| /** The exposed actions callback. */ |
| private ExposedActionsCallback exposedActionsCallback; |
| |
| /** The view context. */ |
| private IViewContext viewContext; |
| |
| /** The redirected eventtopics. */ |
| private HashMap<String, Set<YBeanSlot>> redirectedEventtopics; |
| |
| /** The event handlers. */ |
| private Set<EventHandler> eventHandlers = new HashSet<EventHandler>(); |
| |
| /** The ecview field validation manager. */ |
| private IFieldValidationManager ecviewFieldValidationManager; |
| |
| /** The validation result. */ |
| private Set<IStatus> validationResult; |
| |
| /** |
| * Instantiates a new generic ec view part. |
| */ |
| public GenericECViewPart() { |
| } |
| |
| /** |
| * Setup. |
| */ |
| @SuppressWarnings("serial") |
| @PostConstruct |
| public void setup() { |
| |
| if (yView == null) { |
| Notification.show("View model is not available!", |
| Notification.Type.ERROR_MESSAGE); |
| return; |
| } |
| |
| exposedActionsCallback = new ExposedActionsCallback(); |
| |
| VerticalLayout layout = new VerticalLayout(); |
| parentLayout.addComponent(layout); |
| layout.setSizeFull(); |
| |
| redirectEventTopics(yView); |
| |
| // render the Vaadin UI |
| Map<String, Object> properties = new HashMap<String, Object>(); |
| |
| // register services to be used |
| Map<String, Object> services = new HashMap<String, Object>(); |
| services.put(org.eclipse.osbp.runtime.common.event.IEventBroker.class |
| .getName(), new E4EventBrokerAdapter(e4EventBroker)); |
| if (sharedState != null) { |
| services.put(ISharedStateContext.class.getName(), sharedState); |
| } |
| properties.put(IViewContext.PARAM_SERVICES, services); |
| |
| VaadinRenderer renderer = new VaadinRenderer(); |
| try { |
| viewContext = renderer.render(layout, yView, properties); |
| viewContext.createBeanSlot(BEAN_SLOT__VALIDATION_ERROR, |
| Boolean.class); |
| viewContext.setBean(BEAN_SLOT__VALIDATION_ERROR, Boolean.FALSE); |
| } catch (ContextException e) { |
| LOGGER.error("{}", e); |
| } |
| |
| // bridges the validation from ECView core to Vaaclipse |
| setupValidationBridge(); |
| |
| // register a layout click listener to activate the part. |
| layout.addLayoutClickListener(new LayoutEvents.LayoutClickListener() { |
| @Override |
| public void layoutClick(LayoutClickEvent event) { |
| partService.activate(mPart); |
| } |
| }); |
| |
| setupFocusFieldFeature(); |
| |
| preparePartToolbar(yView); |
| } |
| |
| /** |
| * Setup the focus-field feature. Handles events from event broker and tries |
| * to focus the element. |
| */ |
| protected void setupFocusFieldFeature() { |
| // install an event handler that is responsible to focus elements |
| EventHandler focusEventHandler = new EventHandler() { |
| @Override |
| public void handleEvent(Event event) { |
| String partId = (String) event |
| .getProperty(IE4Topics.PartEvents.PROP_MPART_ID); |
| if (partId != null && partId.equals(mPart.getElementId())) { |
| String fieldId = (String) event |
| .getProperty(IE4Topics.PartEvents.PROP_FIELD_ID); |
| if (fieldId != null && !fieldId.equals("")) { |
| YEmbeddable yElement = (YEmbeddable) viewContext |
| .findModelElement(fieldId); |
| if (yElement instanceof YFocusable) { |
| YView yView = (YView) viewContext.getViewEditpart() |
| .getModel(); |
| yView.setCurrentFocus((YFocusable) yElement); |
| } |
| } |
| } |
| } |
| }; |
| eventHandlers.add(focusEventHandler); |
| |
| e4EventBroker.subscribe(IE4Topics.PartEvents.FOCUS_FIELD_TOPIC, |
| focusEventHandler); |
| } |
| |
| /** |
| * Setup validation bridge. |
| */ |
| protected void setupValidationBridge() { |
| // create a validation enhancer. If an error message is added to the |
| // field validation manager. The enhancer will set the id of the mpart |
| // to the status. |
| ecviewFieldValidationManager = viewContext |
| .getService(IFieldValidationManager.class.getName()); |
| ecviewFieldValidationManager |
| .setEnhancer(new IFieldValidationManager.Enhancer() { |
| @Override |
| public void enhance(IStatus status) { |
| status.putProperty(IStatus.PROP_UI_APPLICATION_ID, |
| mPart.getElementId()); |
| } |
| }); |
| |
| ecviewFieldValidationManager.addListener(this); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.osbp.ecview.core.common.validation.IFieldValidationManager.Listener#validationChanged(org.eclipse.osbp.ecview.core.common.validation.IFieldValidationManager.Event) |
| */ |
| @Override |
| public void validationChanged(IFieldValidationManager.Event event) { |
| // add them to the Vaaclipse status manager |
| IStatusScope scope = statusManager.getScopeFor(mPart); |
| scope.modifyStatus(event.getOldStatus(), event.getNewStatus()); |
| |
| applyValidationResult(); |
| } |
| |
| /** |
| * Remove defined event topics at bean slots. We pass them to custom |
| * handlers which will forward to the view. So we can avoid loosing changed |
| * data. |
| * |
| * @param yView |
| * the y view |
| */ |
| private void redirectEventTopics(YView yView) { |
| |
| // redirect event topics |
| redirectedEventtopics = new HashMap<String, Set<YBeanSlot>>(); |
| for (YBeanSlot yBeanSlot : yView.getBeanSlots()) { |
| String eventTopic = yBeanSlot.getEventTopic(); |
| if (eventTopic != null && !eventTopic.trim().equals("")) { |
| eventTopic = eventTopic.trim(); |
| |
| // events will be forwarded by this view |
| yBeanSlot.setRedirectEvents(true); |
| |
| Set<YBeanSlot> yRedirSlots = redirectedEventtopics |
| .get(eventTopic); |
| if (yRedirSlots == null) { |
| yRedirSlots = new HashSet<YBeanSlot>(); |
| redirectedEventtopics.put(eventTopic, yRedirSlots); |
| } |
| yRedirSlots.add(yBeanSlot); |
| } |
| } |
| |
| if (redirectedEventtopics.size() > 0) { |
| // install event handler for each redirected topic |
| for (String eventTopic : redirectedEventtopics.keySet()) { |
| // install the event handler |
| EventHandler handler = new EventHandler() { |
| @Override |
| public void handleEvent(Event event) { |
| dispatchEventBrokerEvent(event); |
| } |
| }; |
| |
| e4EventBroker.subscribe(eventTopic, handler); |
| eventHandlers.add(handler); |
| } |
| } |
| } |
| |
| /** |
| * Save. |
| * |
| * @param commandId |
| * the command id |
| */ |
| @Persist |
| public void save(@Named("commandId") String commandId) { |
| final Object mainDto = viewContext.getBean(IViewContext.MAIN_BEAN_SLOT); |
| boolean processedProperly = false; |
| try { |
| if (mainDto != null) { |
| new SharedStateUnitOfWork<Object>() { |
| @Override |
| protected Object doExecute() { |
| dtoService.update(mainDto); |
| return null; |
| } |
| }.execute(sharedState); |
| // in case of exception, it is still false |
| processedProperly = true; |
| } else { |
| } |
| } finally { |
| if (processedProperly) { |
| notifyExecuted(toExposedAction(commandId)); |
| } else { |
| notifyCanceled(toExposedAction(commandId)); |
| } |
| } |
| } |
| |
| /** |
| * To exposed action. |
| * |
| * @param commandId |
| * the command id |
| * @return the y exposed action |
| */ |
| private YExposedAction toExposedAction(String commandId) { |
| return commandInfo.get(commandId).action; |
| } |
| |
| /** |
| * Validate. |
| * |
| * @param commandId |
| * the command id |
| */ |
| @Validate |
| public void validate(@Named("commandId") String commandId) { |
| if (commandId != null) { |
| |
| Set<IStatus> oldValidationResult = validationResult != null ? new HashSet<IStatus>( |
| validationResult) : new HashSet<IStatus>(); |
| |
| final Object mainDto = viewContext |
| .getBean(IViewContext.MAIN_BEAN_SLOT); |
| validationResult = dtoService.validate(mainDto, |
| ValidationKind.OTHER, null); |
| |
| for (IStatus status : validationResult) { |
| // set the application id to the status |
| status.putProperty(IStatus.PROP_UI_APPLICATION_ID, |
| mPart.getElementId()); |
| |
| if (status.containsProperty(IStatus.PROP_FIELD_ID) |
| && status.containsProperty(IStatus.PROP_FIELD_I18N_KEY)) { |
| continue; |
| } |
| |
| // fix the field id. Therefore we try to find the yField that is |
| // bound to the property path of javax.validation result. |
| if (!status.containsProperty(IStatus.PROP_FIELD_ID)) { |
| if (status |
| .containsProperty(IStatus.PROP_JAVAX_PROPERTY_PATH)) { |
| String path = (String) status |
| .getProperty(IStatus.PROP_JAVAX_PROPERTY_PATH); |
| |
| // if path points to a collection, then remove all the |
| // collection stuff, since Vaadin can not focus rows |
| if (path.matches(".*\\[\\d*\\].*")) { |
| path = path.substring(0, path.indexOf("[")); |
| } |
| |
| // using a regex to find the yElement: |
| // "beanslot/.*/{propertyPath}" |
| YEmbeddable yElement = (YEmbeddable) viewContext |
| .findBoundField(String.format("%s.*/%s", |
| BindingIdUtil.BEANSLOT, path)); |
| if (yElement != null) { |
| status.putProperty(IStatus.PROP_FIELD_ID, |
| yElement.getId()); |
| status.putProperty(IStatus.PROP_FIELD_I18N_KEY, |
| yElement.getLabelI18nKey()); |
| } |
| } |
| } |
| } |
| |
| // now update the field with the new Status instances |
| // |
| for (IStatus status : oldValidationResult) { |
| String fieldId = (String) status |
| .getProperty(IStatus.PROP_FIELD_ID); |
| if (fieldId != null && !fieldId.trim().equals("")) { |
| YField yField = (YField) viewContext |
| .findModelElement(fieldId); |
| IFieldEditpart editpart = ModelUtil.findEditpart(yField); |
| if (editpart != null) { |
| editpart.removeExternalStatus(status); |
| } |
| } |
| } |
| |
| for (IStatus status : validationResult) { |
| String fieldId = (String) status |
| .getProperty(IStatus.PROP_FIELD_ID); |
| if (fieldId != null && !fieldId.trim().equals("")) { |
| YField yField = (YField) viewContext |
| .findModelElement(fieldId); |
| IFieldEditpart editpart = ModelUtil.findEditpart(yField); |
| if (editpart != null) { |
| editpart.addExternalStatus(status); |
| } |
| } |
| } |
| |
| statusManager.getScopeFor(mPart).modifyStatus(oldValidationResult, |
| validationResult); |
| |
| notifyExternalClicked(toExposedAction(commandId)); |
| |
| applyValidationResult(); |
| } |
| } |
| |
| /** |
| * Apply the validation result to the view. |
| */ |
| protected void applyValidationResult() { |
| // set the validation result |
| viewContext.setBean(BEAN_SLOT__VALIDATION_ERROR, Boolean.FALSE); |
| for (IStatus status : statusManager.getScopeFor(mPart).getAllStatus()) { |
| if (status.isError()) { |
| viewContext.setBean(BEAN_SLOT__VALIDATION_ERROR, Boolean.TRUE); |
| break; |
| } |
| } |
| } |
| |
| /** |
| * Gets the enabled state. |
| * |
| * @param commandId |
| * the command id |
| * @return the enabled state |
| */ |
| @EnabledState |
| public boolean getEnabledState(@Named("commandId") String commandId) { |
| CommandInfo info = findCommandInfo(commandId); |
| if (info == null) { |
| return true; |
| } |
| return info.enabled; |
| } |
| |
| /** |
| * Delete. |
| * |
| * @param commandId |
| * the command id |
| */ |
| @Delete |
| public void delete(@Named("commandId") final String commandId) { |
| final Object mainDto = viewContext.getBean(IViewContext.MAIN_BEAN_SLOT); |
| if (mainDto != null) { |
| AcceptDeleteDialog.showDialog(i18nService, resourceProvider, |
| new Runnable() { |
| @Override |
| public void run() { |
| new SharedStateUnitOfWork<Object>() { |
| @Override |
| protected Object doExecute() { |
| boolean processed = false; |
| try { |
| try { |
| dtoService.delete(mainDto); |
| // in case of exception, it is not |
| // changed |
| processed = true; |
| } catch (DtoServiceException e) { |
| // catch exception, show message and |
| // return |
| Notification.show("", |
| e.getMessage(), |
| Type.ERROR_MESSAGE); |
| } |
| } finally { |
| if (processed) { |
| notifyExecuted(toExposedAction(commandId)); |
| } else { |
| notifyCanceled(toExposedAction(commandId)); |
| } |
| } |
| return null; |
| } |
| }.execute(sharedState); |
| } |
| }, new ActionCanceledAdapter(toExposedAction(commandId))); |
| } else { |
| notifyCanceled(toExposedAction(commandId)); |
| } |
| } |
| |
| /** |
| * Notifies the action about cancel. |
| * |
| * @param yAction |
| * the y action |
| */ |
| protected void notifyCanceled(final YExposedAction yAction) { |
| ActionCanceledAdapter.notify(yAction); |
| } |
| |
| /** |
| * Notifies the action about executed. |
| * |
| * @param yAction |
| * the y action |
| */ |
| protected void notifyExecuted(final YExposedAction yAction) { |
| ActionExecutedAdapter.notify(yAction); |
| } |
| |
| /** |
| * Notifies the action about external clicked. |
| * |
| * @param yAction |
| * the y action |
| */ |
| protected void notifyExternalClicked(final YExposedAction yAction) { |
| ActionExternalClickedAdapter.notify(yAction); |
| } |
| |
| /** |
| * Reload. |
| * |
| * @param commandId |
| * the command id |
| */ |
| @Load |
| public void reload(@Named("commandId") final String commandId) { |
| final Object mainDto = viewContext.getBean(IViewContext.MAIN_BEAN_SLOT); |
| if (mainDto != null) { |
| boolean isDirty = false; |
| try { |
| isDirty = DtoUtils.isDirty(mainDto); |
| } catch (IllegalAccessException e) { |
| // nothing to do |
| } |
| |
| // if there is no dirty indicator, or the record is not dirty, |
| // reload the data |
| if (!isDirty) { |
| new SharedStateUnitOfWork<Object>() { |
| @Override |
| protected Object doExecute() { |
| boolean processed = false; |
| try { |
| dtoService.reload(mainDto); |
| // in case of exception, it is not changed |
| processed = true; |
| } finally { |
| if (processed) { |
| statusManager.getScopeFor(mPart).clearStatus(); |
| |
| notifyExecuted(toExposedAction(commandId)); |
| } else { |
| notifyCanceled(toExposedAction(commandId)); |
| } |
| } |
| return null; |
| } |
| }.execute(sharedState); |
| } else { |
| AcceptReloadDialog.showDialog( |
| i18nService, |
| resourceProvider, |
| new Runnable() { |
| @Override |
| public void run() { |
| new SharedStateUnitOfWork<Object>() { |
| @Override |
| protected Object doExecute() { |
| boolean processed = false; |
| try { |
| dtoService.reload(mainDto); |
| // in case of exception, it is not |
| // changed |
| processed = true; |
| } finally { |
| if (processed) { |
| statusManager |
| .getScopeFor(mPart) |
| .clearStatus(); |
| |
| notifyExecuted(toExposedAction(commandId)); |
| } else { |
| notifyCanceled(toExposedAction(commandId)); |
| } |
| } |
| return null; |
| } |
| }.execute(sharedState); |
| } |
| }, |
| new ActionCanceledAdapter(toExposedAction(commandId))); |
| } |
| } else { |
| notifyCanceled(toExposedAction(commandId)); |
| } |
| } |
| |
| /** |
| * If a command was executed, the original actionId from the ECView view |
| * (YView) will be passed here. |
| * |
| * @param yAction |
| * the y action |
| */ |
| @Callback |
| public void commandExecuted(YExposedAction yAction) { |
| if (yAction != null) { |
| // we are going to forward the execution of the action to the ecView |
| // exposed action |
| // final YExposedAction yAction = (YExposedAction) action; |
| yAction.setExternalClickTime(new Date().getTime()); |
| |
| // check if dto is dirty |
| // |
| boolean isDirty = false; |
| final Object mainDto = viewContext |
| .getBean(IViewContext.MAIN_BEAN_SLOT); |
| if (mainDto != null) { |
| try { |
| isDirty = DtoUtils.isDirty(mainDto); |
| } catch (IllegalAccessException e) { |
| // nothing to do -> if there is not dirty flag, we ignore |
| // this |
| } |
| } |
| |
| // to notify about executed or canceled state, first check if the |
| // state is dirty |
| if (isDirty && yAction.isCheckDirty()) { |
| // show accept dialog |
| AcceptLoosingDataDialog.showDialog(i18nService, |
| resourceProvider, new ActionExecutedAdapter(yAction), |
| new ActionCanceledAdapter(yAction)); |
| } else { |
| notifyExecuted(yAction); |
| } |
| } |
| } |
| |
| /** |
| * Prepares the toolbar for the view. |
| * |
| * @param yView |
| * the y view |
| */ |
| private void preparePartToolbar(YView yView) { |
| MToolBar mToolbar = mPart.getToolbar(); |
| clearToolbar(mToolbar); |
| |
| for (YExposedAction yAction : yView.getExposedActions()) { |
| |
| // register the exposed actions callback to handle enabled state |
| yAction.eAdapters().add(exposedActionsCallback); |
| |
| MHandledToolItem toolItem = null; |
| if (yAction.getExternalCommandId() == null) { |
| toolItem = createToolItem(yAction, |
| IE4Constants.COMMAND_DEFAULT_PART_CALLBACK, |
| yAction.isInitialEnabled()); |
| } else if (yAction.getExternalCommandId() != null) { |
| toolItem = createToolItem(yAction, |
| yAction.getExternalCommandId(), |
| yAction.isInitialEnabled()); |
| } |
| |
| if (toolItem != null) { |
| mToolbar.getChildren().add(toolItem); |
| // set default after rendering |
| toolItem.setEnabled(yAction.isInitialEnabled()); |
| } |
| } |
| } |
| |
| /** |
| * Clear toolbar. |
| * |
| * @param mToolbar |
| * the m toolbar |
| */ |
| protected void clearToolbar(MToolBar mToolbar) { |
| // bug in Vaaclipse -> Iterate to remove all |
| for (Iterator<MToolBarElement> iterator = mToolbar.getChildren() |
| .iterator(); iterator.hasNext();) { |
| iterator.next(); |
| iterator.remove(); |
| } |
| } |
| |
| /** |
| * Creates handled tool items for the given exposed action and the |
| * commandId. |
| * |
| * @param yAction |
| * the y action |
| * @param commandId |
| * the command id |
| * @param initialEnabled |
| * the initial enabled |
| * @return the m handled tool item |
| */ |
| private MHandledToolItem createToolItem(YExposedAction yAction, |
| String commandId, boolean initialEnabled) { |
| MCommand command = findCommand(mPart, commandId); |
| if (command == null) { |
| LOGGER.error("No action created for " + yAction.getId() |
| + " since command missing: " + commandId); |
| return null; |
| } |
| |
| MHandledToolItem toolItem = MMenuFactory.INSTANCE |
| .createHandledToolItem(); |
| toolItem.setCommand(command); |
| |
| String keyBinding = keyBindingService.getKeyBindingString(mPart, |
| commandId); |
| String tooltip = i18nService.getValue(yAction.getLabelI18nKey(), |
| Locale.getDefault()); |
| toolItem.setTooltip(tooltip + " " + keyBinding); |
| toolItem.setIconURI(i18nService.getValue(yAction.getIcon(), |
| Locale.getDefault())); |
| toolItem.setVisible(true); |
| toolItem.setToBeRendered(true); |
| |
| commandInfo.put(commandId, new CommandInfo(yAction, toolItem, |
| initialEnabled, commandId)); |
| |
| return toolItem; |
| } |
| |
| /** |
| * Tries to find the command with the specified id. |
| * |
| * @param mPart |
| * the m part |
| * @param id |
| * the id |
| * @return the m command |
| */ |
| private MCommand findCommand(MPart mPart, String id) { |
| EModelService modelService = eclipseContext.get(EModelService.class); |
| List<MCommand> commands = modelService.findElements( |
| eclipseContext.get(MApplication.class), id, MCommand.class, |
| Collections.<String> emptyList()); |
| MCommand command = null; |
| if (commands.size() > 0) { |
| command = commands.get(0); |
| } |
| return command; |
| } |
| |
| /** |
| * Dispose. |
| */ |
| @PreDestroy |
| public void dispose() { |
| try { |
| if (redirectedEventtopics != null) { |
| redirectedEventtopics.clear(); |
| redirectedEventtopics = null; |
| } |
| |
| exposedActionsCallback = null; |
| |
| if (eventHandlers != null) { |
| for (EventHandler handler : eventHandlers) { |
| e4EventBroker.unsubscribe(handler); |
| |
| } |
| eventHandlers.clear(); |
| eventHandlers = null; |
| } |
| |
| if (viewContext != null) { |
| viewContext.dispose(); |
| } |
| |
| if (ecviewFieldValidationManager != null) { |
| ecviewFieldValidationManager.dispose(); |
| ecviewFieldValidationManager.removeListener(this); |
| ecviewFieldValidationManager = null; |
| } |
| |
| } catch (Exception ex) { |
| ex.printStackTrace(); |
| } |
| } |
| |
| /** |
| * Dispatches events from the event broker to the proper bean slots. |
| * |
| * @param event |
| * the event |
| */ |
| protected void dispatchEventBrokerEvent(final Event event) { |
| if (redirectedEventtopics == null) { |
| return; |
| } |
| final String eventTopic = topicNormalizer.unwrapTopic(event.getTopic()); |
| if (!redirectedEventtopics.containsKey(eventTopic)) { |
| return; |
| } |
| |
| final Object newBean = event.getProperty(IEventBroker.DATA); |
| |
| // create a runnable processing the set operations |
| Runnable doRunnable = new Runnable() { |
| @Override |
| public void run() { |
| |
| resetStatus(); |
| |
| for (YBeanSlot yBeanSlot : redirectedEventtopics |
| .get(eventTopic)) { |
| final ISlot slot = viewContext.getBeanSlot(yBeanSlot |
| .getName()); |
| |
| // TODO workaround for databinding -> New instance may be |
| // polymorphic brother of the current instance. And if |
| // binding |
| // the new instance, numeric field will not become unbound. |
| // So |
| // lets set a new instance of current set instance before |
| // setting the new entry. |
| Object oldValue = slot.getValue(); |
| if (oldValue != null) { |
| Class<?> valueClass = oldValue.getClass(); |
| try { |
| // now all fields will become unbound from the |
| // current |
| // instance |
| slot.setValue(valueClass.newInstance()); |
| } catch (Exception e) { |
| LOGGER.warn("Could not reset the value by {}", |
| valueClass.getName()); |
| } |
| } |
| |
| slot.setValue(newBean); |
| } |
| } |
| }; |
| |
| if (isBeanslotDirty(eventTopic, newBean)) { |
| // show an accept loosing data dialog |
| AcceptLoosingDataDialog.showDialog(i18nService, resourceProvider, |
| doRunnable, null); |
| } else { |
| doRunnable.run(); |
| } |
| |
| } |
| |
| /** |
| * Returns true, if one of the bean slots addressed by the eventTopic is |
| * dirty. |
| * |
| * @param eventTopic |
| * the event topic |
| * @param newBean |
| * the new bean |
| * @return true, if is beanslot dirty |
| */ |
| protected boolean isBeanslotDirty(String eventTopic, Object newBean) { |
| boolean dirty = false; |
| for (YBeanSlot yBeanSlot : redirectedEventtopics.get(eventTopic)) { |
| final ISlot slot = viewContext.getBeanSlot(yBeanSlot.getName()); |
| Object currentBean = slot.getValue(); |
| if (currentBean != null && currentBean != newBean) { |
| try { |
| dirty = DtoUtils.isDirty(currentBean); |
| if (dirty) { |
| // dirty found and leave |
| break; |
| } |
| } catch (IllegalAccessException e) { |
| // if there is no dirty flag, we just ignore it |
| } |
| } |
| } |
| return dirty; |
| } |
| |
| /** |
| * Resets the status in the status manager. |
| */ |
| protected void resetStatus() { |
| statusManager.getScopeFor(mPart).clearStatus(); |
| } |
| |
| /** |
| * Forwards the enabled state to the e4 tool item. |
| */ |
| private class ExposedActionsCallback extends AdapterImpl { |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.emf.common.notify.impl.AdapterImpl#notifyChanged(org.eclipse.emf.common.notify.Notification) |
| */ |
| @Override |
| public void notifyChanged(org.eclipse.emf.common.notify.Notification msg) { |
| if (msg.getEventType() == org.eclipse.emf.common.notify.Notification.SET) { |
| if (msg.getFeature() == CoreModelPackage.Literals.YENABLE__ENABLED) { |
| YExposedAction yAction = (YExposedAction) msg.getNotifier(); |
| CommandInfo info = findCommandInfo(yAction); |
| if (info != null) { |
| boolean newEnabled = msg.getNewBooleanValue(); |
| info.toolItem.setEnabled(newEnabled); |
| info.enabled = newEnabled; |
| } |
| } else if (msg.getFeature() == CoreModelPackage.Literals.YEXPOSED_ACTION__INTERNAL_CLICK_TIME) { |
| YExposedAction yAction = (YExposedAction) msg.getNotifier(); |
| CommandInfo info = findCommandInfo(yAction); |
| if (info != null) { |
| MHandledToolItem handledItem = (MHandledToolItem) info.toolItem; |
| IPartItemExecutionService service = mPart.getContext() |
| .get(IPartItemExecutionService.class); |
| if (service != null |
| && service.canExecuteItem(handledItem)) { |
| // notify the exposed action about the external |
| // click |
| yAction.setExternalClickTime(new Date().getTime()); |
| service.executeItem(handledItem); |
| } else { |
| // notify the action about the cancel |
| ActionCanceledAdapter.notify(yAction); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * Notifies the action about a cancel. |
| */ |
| private static class ActionCanceledAdapter implements Runnable { |
| |
| /** The action. */ |
| private final YExposedAction action; |
| |
| /** |
| * Notify. |
| * |
| * @param action |
| * the action |
| */ |
| public static void notify(YExposedAction action) { |
| action.setCanceledNotificationTime(new Date().getTime()); |
| } |
| |
| /** |
| * Instantiates a new action canceled adapter. |
| * |
| * @param action |
| * the action |
| */ |
| public ActionCanceledAdapter(YExposedAction action) { |
| super(); |
| this.action = action; |
| } |
| |
| /* (non-Javadoc) |
| * @see java.lang.Runnable#run() |
| */ |
| @Override |
| public void run() { |
| notify(action); |
| } |
| } |
| |
| /** |
| * Notifies the action about their proper execution. |
| */ |
| private static class ActionExecutedAdapter implements Runnable { |
| |
| /** The action. */ |
| private final YExposedAction action; |
| |
| /** |
| * Notify. |
| * |
| * @param action |
| * the action |
| */ |
| public static void notify(YExposedAction action) { |
| action.setExecutedNotificationTime(new Date().getTime()); |
| } |
| |
| /** |
| * Instantiates a new action executed adapter. |
| * |
| * @param action |
| * the action |
| */ |
| public ActionExecutedAdapter(YExposedAction action) { |
| super(); |
| this.action = action; |
| } |
| |
| /* (non-Javadoc) |
| * @see java.lang.Runnable#run() |
| */ |
| @Override |
| public void run() { |
| notify(action); |
| } |
| } |
| |
| /** |
| * Notifies the action about the external click. |
| */ |
| private static class ActionExternalClickedAdapter implements Runnable { |
| |
| /** The action. */ |
| private final YExposedAction action; |
| |
| /** |
| * Notify. |
| * |
| * @param action |
| * the action |
| */ |
| public static void notify(YExposedAction action) { |
| action.setExternalClickTime(new Date().getTime()); |
| } |
| |
| /** |
| * Instantiates a new action external clicked adapter. |
| * |
| * @param action |
| * the action |
| */ |
| @SuppressWarnings("unused") |
| public ActionExternalClickedAdapter(YExposedAction action) { |
| super(); |
| this.action = action; |
| } |
| |
| /* (non-Javadoc) |
| * @see java.lang.Runnable#run() |
| */ |
| @Override |
| public void run() { |
| notify(action); |
| } |
| } |
| |
| /** |
| * A pojo to keep related objects together. Will be used by the |
| * GenericECViewPart and also by handlers to determine enabled state. |
| */ |
| private static class CommandInfo { |
| |
| /** The action. */ |
| private YExposedAction action; |
| |
| /** The tool item. */ |
| private MToolItem toolItem; |
| |
| /** The enabled. */ |
| // is used to return the enabled state for a request from handler |
| private boolean enabled; |
| |
| /** The command id. */ |
| private String commandId; |
| |
| /** |
| * Instantiates a new command info. |
| * |
| * @param action |
| * the action |
| * @param toolItem |
| * the tool item |
| * @param enabled |
| * the enabled |
| * @param commandId |
| * the command id |
| */ |
| public CommandInfo(YExposedAction action, MToolItem toolItem, |
| boolean enabled, String commandId) { |
| super(); |
| this.action = action; |
| this.toolItem = toolItem; |
| this.enabled = enabled; |
| this.commandId = commandId; |
| } |
| |
| } |
| |
| /** |
| * Returns the command info for the given action. |
| * |
| * @param yAction |
| * the y action |
| * @return the command info |
| */ |
| protected CommandInfo findCommandInfo(final YExposedAction yAction) { |
| try { |
| return commandInfo.values().stream() |
| .filter(e -> e.action == yAction).findFirst().get(); |
| } catch (NoSuchElementException e) { |
| return null; |
| } |
| } |
| |
| /** |
| * Returns the command info for the given toolItem. |
| * |
| * @param toolItem |
| * the tool item |
| * @return the command info |
| */ |
| protected CommandInfo findCommandInfo(MToolBarElement toolItem) { |
| try { |
| return commandInfo.values().stream() |
| .filter(e -> e.toolItem == toolItem).findFirst().get(); |
| } catch (NoSuchElementException e) { |
| return null; |
| } |
| } |
| |
| /** |
| * Returns the command info for the given commandId. |
| * |
| * @param commandId |
| * the command id |
| * @return the command info |
| */ |
| protected CommandInfo findCommandInfo(final String commandId) { |
| return commandInfo.get(commandId); |
| } |
| |
| } |