package org.ovirt.engine.ui.common.presenter;
import java.util.List;
import java.util.logging.Logger;
import org.ovirt.engine.ui.common.uicommon.model.DetailModelProvider;
import org.ovirt.engine.ui.common.uicommon.model.SearchableDetailModelProvider;
import org.ovirt.engine.ui.common.widget.table.ActionTable;
import org.ovirt.engine.ui.common.widget.table.HasActionTable;
import org.ovirt.engine.ui.common.widget.table.OrderedMultiSelectionModel;
import org.ovirt.engine.ui.uicommonweb.models.HasEntity;
import org.ovirt.engine.ui.uicommonweb.models.ListWithDetailsModel;
import org.ovirt.engine.ui.uicompat.PropertyChangedEventArgs;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.event.shared.EventBus;
import com.google.gwt.event.shared.GwtEvent.Type;
import com.google.gwt.user.cellview.client.LoadingStateChangeEvent.LoadingState;
import com.gwtplatform.mvp.client.View;
import com.gwtplatform.mvp.client.proxy.PlaceManager;
import com.gwtplatform.mvp.client.proxy.RevealContentHandler;
import com.gwtplatform.mvp.client.proxy.TabContentProxyPlace;
import com.gwtplatform.mvp.shared.proxy.PlaceRequest;
/**
* Base class for presenters representing sub tabs that react to item selection changes within main tab presenters.
*
* @param T Main tab table row data type.
* @param M Main model type (extends ListWithDetailsModel)
* @param D Detail model type extends HasEntity
* @param V View type (extends AbstractSubTabPresenter.ViewDef)
* @param P Proxy type (extends TabContentProxyPlace)
*/
public abstract class AbstractSubTabPresenter<T, M extends ListWithDetailsModel, D extends HasEntity,
V extends AbstractSubTabPresenter.ViewDef<T>, P extends TabContentProxyPlace<?>>
extends AbstractTabPresenter<V, P> implements MainTabSelectedItemChangeListener<T> {
// TODO(vszocs) use HasActionTable<I> instead of raw type HasActionTable, this will
// require adding new type parameter to presenter (do later as part of refactoring)
@SuppressWarnings("rawtypes")
public interface ViewDef<T> extends View, HasActionTable {
/**
* Notifies the view that the main tab item selection has changed.
*/
void setMainTabSelectedItem(T selectedItem);
}
private static final Logger logger = Logger.getLogger(AbstractSubTabPresenter.class.getName());
private final PlaceManager placeManager;
private final DetailModelProvider<M, D> modelProvider;
private final AbstractMainTabSelectedItems<T> selectedMainItems;
/**
* @param view View type (extends AbstractSubTabPresenter.ViewDef<T>)
* @param proxy Proxy type (extends TabContentProxyPlace)
* @param modelProvider DetailModelProvider<M, D> - M - Main model type (extends ListWithDetailsModel),
* D - Detail model type extends HasEntity
*/
public AbstractSubTabPresenter(EventBus eventBus, V view, P proxy,
PlaceManager placeManager, DetailModelProvider<M, D> modelProvider,
AbstractMainTabSelectedItems<T> selectedMainItems,
Type<RevealContentHandler<?>> slot) {
super(eventBus, view, proxy, slot);
this.placeManager = placeManager;
this.modelProvider = modelProvider;
this.selectedMainItems = selectedMainItems;
}
@Override
protected ActionTable<?> getTable() {
return getView().getTable();
}
@Override
protected void onBind() {
super.onBind();
OrderedMultiSelectionModel<?> tableSelectionModel = getTable() != null ? getTable().getSelectionModel() : null;
if (tableSelectionModel != null) {
registerHandler(tableSelectionModel.addSelectionChangeHandler(event -> {
// Update detail model selection
updateDetailModelSelection();
}));
}
initializeHandlers();
getSelectedMainItems().registerListener(this);
itemChanged(getSelectedMainItems().getSelectedItem());
}
@Override
public void itemChanged(T item) {
if (item != null && getView().asWidget().isVisible()) {
getView().setMainTabSelectedItem(item);
}
}
/**
* Updates the detail model with items currently selected in the main tab.
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
void updateDetailModelSelection() {
if (modelProvider instanceof SearchableDetailModelProvider) {
((SearchableDetailModelProvider) modelProvider).setSelectedItems(getSelectedItems());
}
}
/**
* We use manual reveal since we want to prevent users from accessing this presenter when there is nothing selected
* in the main tab.
*/
@Override
public boolean useManualReveal() {
return true;
}
@Override
protected void onReveal() {
super.onReveal();
// Notify model provider that the tab has been revealed
modelProvider.onSubTabSelected();
}
@Override
protected void onHide() {
super.onHide();
modelProvider.onSubTabDeselected();
}
@Override
protected void onReset() {
super.onReset();
// Clear table selection before starting
clearSelection();
if (getTable() != null) {
getTable().resetScrollPosition();
}
}
@Override
public void prepareFromRequest(PlaceRequest request) {
super.prepareFromRequest(request);
// Reveal presenter only when there is something selected in the main tab
if (selectedMainItems.hasSelection()) {
getProxy().manualReveal(this);
} else {
getProxy().manualRevealFailed();
placeManager.revealPlace(getMainTabRequest());
}
}
/**
* Returns the place request associated with the main tab presenter for this sub tab.
* <p>
* Will be revealed when the user tries to access this sub tab while there is nothing selected in the main tab.
*/
protected abstract PlaceRequest getMainTabRequest();
/**
* Returns items currently selected in the table or {@code null} if the sub tab view has no table widget associated.
*/
protected List<?> getSelectedItems() {
return getTable() != null ? getTable().getSelectionModel().getSelectedList() : null;
}
/**
* Deselects any items currently selected in the table. Does nothing if the sub tab view has no table widget
* associated.
*/
protected void clearSelection() {
if (getTable() != null) {
getTable().getSelectionModel().clear();
}
}
public void initializeHandlers() {
// Notify view when the entity of the detail model changes
modelProvider.getModel().getEntityChangedEvent().addListener((ev, sender, args) -> {
Object entity = modelProvider.getModel().getEntity();
if (entity != null) {
onDetailModelEntityChange(entity);
}
});
// Notify view when the detail model indicates progress
modelProvider.getModel().getPropertyChangedEvent().addListener((ev, sender, args) -> {
if (PropertyChangedEventArgs.PROGRESS.equals(args.propertyName)) {
if (modelProvider.getModel().getProgress() != null) {
Scheduler.get().scheduleDeferred(() -> {
if (getTable() != null) {
getTable().setLoadingState(LoadingState.LOADING);
}
});
}
}
});
}
/**
* Override this method in case the detail model entity type is different from main model item type.
*/
@SuppressWarnings("unchecked")
protected void onDetailModelEntityChange(Object entity) {
try {
getView().setMainTabSelectedItem((T) entity);
} catch (ClassCastException ex) {
// Detail model entity type is different from main model item type.
// This usually happens with synthetic item types that wrap multiple
// logical entities into single type. Since views can typically edit
// a single item type, we can do nothing here.
logger.warning("Detail model entity type is different from main model item type"); //$NON-NLS-1$
}
}
protected DetailModelProvider<M, D> getModelProvider() {
return modelProvider;
}
protected AbstractMainTabSelectedItems<T> getSelectedMainItems() {
return selectedMainItems;
}
}