package org.ovirt.engine.ui.common.uicommon.model; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.ovirt.engine.ui.common.presenter.AbstractModelBoundPopupPresenterWidget; import org.ovirt.engine.ui.uicommonweb.UICommand; import org.ovirt.engine.ui.uicommonweb.models.ConfirmationModel; import org.ovirt.engine.ui.uicommonweb.models.IModel; import org.ovirt.engine.ui.uicommonweb.models.Model; import org.ovirt.engine.ui.uicompat.PropertyChangedEventArgs; import com.google.gwt.event.shared.EventBus; import com.google.inject.Provider; import com.gwtplatform.mvp.client.proxy.RevealRootPopupContentEvent; /** * Contains some logic for handling window model changes for a given source model. * Contains the code that actually reveals the popups via GWTP. * * TODO rename to WindowModelPopupHandler * TODO this class is just a ball of stuff with no one well-defined purpose. Needs a refactor. * * <p> * Uses {@link ModelBoundPopupResolver} for resolving popup widgets from dialog models. * * @param <M> Model type. */ public class ModelBoundPopupHandler<M extends IModel> { private final ModelBoundPopupResolver<M> popupResolver; private final EventBus eventBus; private final Set<String> windowPropertyNames = new HashSet<>(); private final Set<String> confirmWindowPropertyNames = new HashSet<>(); private Map<String, AbstractModelBoundPopupPresenterWidget<?, ?>> windowPopupInstances = new HashMap<>(); private Map<String, AbstractModelBoundPopupPresenterWidget<?, ?>> confirmWindowPopupInstances = new HashMap<>(); private Provider<? extends AbstractModelBoundPopupPresenterWidget<? extends ConfirmationModel, ?>> defaultConfirmPopupProvider; public ModelBoundPopupHandler(ModelBoundPopupResolver<M> popupResolver, EventBus eventBus) { this.popupResolver = popupResolver; this.eventBus = eventBus; init(); } void init() { windowPropertyNames.addAll(Arrays.asList(popupResolver.getWindowPropertyNames())); confirmWindowPropertyNames.addAll(Arrays.asList(popupResolver.getConfirmWindowPropertyNames())); for (String propName : windowPropertyNames) { windowPopupInstances.put(propName, null); } for (String propName : confirmWindowPropertyNames) { confirmWindowPopupInstances.put(propName, null); } } /** * Adds a property change listener to the model that responds when a new window model is set. * * TODO rename addWindowModelChangeListener */ public void addDialogModelListener(final M model) { hideAndClearAllPopups(); model.getPropertyChangedEvent().addListener((ev, sender, args) -> { String propName = args.propertyName; if (windowPropertyNames.contains(propName)) { handleWindowModelChange(model, propName, windowPopupInstances.get(propName), false); } else if (confirmWindowPropertyNames.contains(propName)) { handleWindowModelChange(model, propName, confirmWindowPopupInstances.get(propName), true); } }); } public void setDefaultConfirmPopupProvider( Provider<? extends AbstractModelBoundPopupPresenterWidget<? extends ConfirmationModel, ?>> defaultConfirmPopupProvider) { this.defaultConfirmPopupProvider = defaultConfirmPopupProvider; } // TODO this should be redesigned -- way too complex. GS @SuppressWarnings("unchecked") void handleWindowModelChange(M sourceModel, String propertyName, AbstractModelBoundPopupPresenterWidget<?, ?> currentPopup, boolean isConfirmation) { Model windowModel = isConfirmation ? popupResolver.getConfirmWindowModel(sourceModel, propertyName) : popupResolver.getWindowModel(sourceModel, propertyName); // Reveal new popup if (windowModel != null && currentPopup == null) { // 1. Resolve AbstractModelBoundPopupPresenterWidget<?, ?> newPopup = null; UICommand lastExecutedCommand = sourceModel.getLastExecutedCommand(); if (windowModel instanceof ConfirmationModel) { // Resolve confirmation popup newPopup = popupResolver.getConfirmModelPopup(sourceModel, lastExecutedCommand); if (newPopup == null && defaultConfirmPopupProvider != null) { // Fall back to basic confirmation popup newPopup = defaultConfirmPopupProvider.get(); } } else { // Resolve main popup newPopup = popupResolver.getModelPopup(sourceModel, lastExecutedCommand, windowModel); } // 2. Reveal if (newPopup != null) { revealAndAssignPopup(windowModel, propertyName, (AbstractModelBoundPopupPresenterWidget<Model, ?>) newPopup, isConfirmation); } else { if (isConfirmation) { popupResolver.clearConfirmWindowModel(sourceModel, propertyName); } else { popupResolver.clearWindowModel(sourceModel, propertyName); } } } else if (windowModel == null && currentPopup != null) { hideAndClearPopup(propertyName, currentPopup, isConfirmation); } } /** * Reveals the popup (tells GWTP to show it). */ <T extends Model> void revealPopup(final T model, final AbstractModelBoundPopupPresenterWidget<T, ?> popup) { assert model != null : "Popup model must not be null"; //$NON-NLS-1$ // Initialize popup popup.init(model); // Add "progress" property change handler to Window model model.getPropertyChangedEvent().addListener((ev, sender, args) -> { if (PropertyChangedEventArgs.PROGRESS.equals(args.propertyName)) { updatePopupProgress(model, popup); } }); updatePopupProgress(model, popup); // Reveal popup RevealRootPopupContentEvent.fire(eventBus, popup); } <T extends Model> void updatePopupProgress(T model, AbstractModelBoundPopupPresenterWidget<T, ?> popup) { if (model.getProgress() != null) { popup.startProgress(model.getProgress().getCurrentOperation()); } else { popup.stopProgress(); } } /** * Reveals the popup (tells GWTP to show it) and remembers its reference, * so that it can be closed (hidden) later on. */ protected <T extends Model> void revealAndAssignPopup(T model, String propertyName, AbstractModelBoundPopupPresenterWidget<T, ?> popup, boolean isConfirm) { revealPopup(model, popup); // Assign popup reference if (isConfirm) { confirmWindowPopupInstances.put(propertyName, popup); } else { windowPopupInstances.put(propertyName, popup); } } /** * Hides a popup and clears its reference, so that another popup can be opened. */ protected void hideAndClearPopup(String propertyName, AbstractModelBoundPopupPresenterWidget<?, ?> popup, boolean isConfirm) { popup.hideAndUnbind(); // Clear popup reference if (isConfirm) { confirmWindowPopupInstances.put(propertyName, null); } else { windowPopupInstances.put(propertyName, null); } } /** * Hides confirmation and window popups and clears their references. * * TODO(vs) this method existed so that all application popups can be * shown again after auto-logout and re-login without having to reload * WebAdmin page in browser. With SSO in place (no more GWT UI specific * login screen), this use case is irrelevant and this method should be * removed. */ void hideAndClearAllPopups() { for (String propName : windowPropertyNames) { AbstractModelBoundPopupPresenterWidget<?, ?> popup = windowPopupInstances.get(propName); if (popup != null) { hideAndClearPopup(propName, popup, false); } } for (String propName : confirmWindowPropertyNames) { AbstractModelBoundPopupPresenterWidget<?, ?> confirmPopup = confirmWindowPopupInstances.get(propName); if (confirmPopup != null) { hideAndClearPopup(propName, confirmPopup, true); } } } }