/**
* Copyright (C) 2013 DaiKit.com - daikit4gxt module (admin@daikit.com)
*
* Project home : http://code.daikit.com/daikit4gxt
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.daikit.daikit4gxt.client.ui.popup;
import com.daikit.commons.shared.bean.AbstractDkBeanWithId;
import com.daikit.commons.shared.bean.AbstractDkModifiableBeanWithId;
import com.daikit.commons.shared.exception.DkUnsupportedMethodException;
import com.daikit.commons.shared.miscs.Couple;
import com.daikit.daikit4gxt.client.DkMain;
import com.daikit.daikit4gxt.client.action.BaseAction;
import com.daikit.daikit4gxt.client.action.BaseAsyncCallback;
import com.daikit.daikit4gxt.client.editor.DkClearValidationVisitor;
import com.daikit.daikit4gxt.client.editor.DkPreFlushVisitor;
import com.daikit.daikit4gxt.client.editor.DkSimpleBeanEditorDriver;
import com.daikit.daikit4gxt.client.log.BaseLogger;
import com.daikit.daikit4gxt.client.ui.UIInvalidatable;
import com.google.gwt.editor.client.Editor;
import com.google.gwt.user.client.Timer;
import com.sencha.gxt.data.shared.ListStore;
import com.sencha.gxt.data.shared.TreeStore;
/**
* A class to extend for Dialog box for edition or creation of bean
*
* @author tcaselli
* @version $Revision$ Last modifier: $Author$ Last commit: $Date$
* @param <BEANTYPE>
* edited bean type
* @param <EDITORTYPE>
* {@link Editor} type (editor for type B)
* @param <STORETYPE>
* the type of the grid to which the bean should then bean added or updated
*/
public abstract class MyEditCreateProxyPopup<BEANTYPE extends AbstractDkBeanWithId, STORETYPE extends AbstractDkBeanWithId, EDITORTYPE extends Editor<BEANTYPE>>
extends MyFormDialog implements UIInvalidatable
{
protected BaseLogger log = BaseLogger.getLog(getClass());
private ListStore<STORETYPE> store;
private TreeStore<STORETYPE> treeStore; // only one of store and treeStore is useful at the same time
private final EDITORTYPE editor;
private boolean isCreation = false;
private BEANTYPE model;
private BEANTYPE newInstanceForCreation;
protected STORETYPE treeParentModel;
protected int index = 0;
protected MyEditCreatePopupShowCallback<BEANTYPE, STORETYPE, EDITORTYPE> callback;
@SuppressWarnings("rawtypes")
private DkSimpleBeanEditorDriver driver;
protected String labelCorrectErrorsTitle = DkMain.i18n().error_validation_popup_correct_fields_before_save_title();
protected String labelCorrectErrorsMessage = DkMain.i18n().error_validation_popup_correct_fields_before_save_message();
protected MyEditCreateProxyPopup(final EDITORTYPE editor)
{
super(false, 1, DisplayableButton.SAVE, DisplayableButton.RESET, DisplayableButton.CANCEL);
this.editor = editor;
}
/**
* Use one of these methods instead:
* <ul>
* <li>{@link #showForUpdate(ListStore, AbstractDkBeanWithId)}</li>
* <li>{@link #showForUpdate(TreeStore, AbstractDkBeanWithId)}</li>
* <li>
* {@link #showForUpdate(ListStore, AbstractDkBeanWithId)}</li>
* <li>
* {@link #showForUpdate(TreeStore, AbstractDkBeanWithId)}</li>
* <li>{@link #showForCreation()}</li>
* <li>{@link #showForCreation(ListStore)}</li>
* <li>
* {@link #showForCreation(MyEditCreatePopupShowCallback)}</li>
* <li>{@link #showForCreation(ListStore, MyEditCreatePopupShowCallback)}</li>
* <li>
* {@link #showForCreation(TreeStore, AbstractDkBeanWithId)}</li>
* </li>
* <li>
* {@link #showForCreation(TreeStore, AbstractDkBeanWithId, int)}</li>
* </li>
* <li>
* {@link #showForCreation(TreeStore, AbstractDkBeanWithId, MyEditCreatePopupShowCallback)}</li>
* </li>
* <li>
* {@link #showForCreation(TreeStore, AbstractDkBeanWithId, int, MyEditCreatePopupShowCallback)}</li>
* </li>
* </ul>
*/
@Deprecated
@Override
public void show()
{
throw new DkUnsupportedMethodException("This method must not be called directly. See javadoc for details.");
}
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
// SHOW FOR UPDATE
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
/**
* Show the Dialog box for edition of the given model with no callback
*
* @param store
* the Store
* @param model
* the model to be edited
*/
public void showForUpdate(final ListStore<STORETYPE> store, final BEANTYPE model)
{
showForUpdate(store, model, null);
}
/**
* Show the Dialog box for edition of the given model with no callback
*
* @param treeStore
* the TreeStore
* @param model
* the model to be edited
*/
public void showForUpdate(final TreeStore<STORETYPE> treeStore, final BEANTYPE model)
{
showForUpdate(treeStore, model, null);
}
/**
* Show the Dialog box for edition of the given model
*
* @param store
* the Store
* @param model
* the model to be edited
* @param callback
* the {@link MyEditCreatePopupShowCallback} callback
*/
public void showForUpdate(final ListStore<STORETYPE> store, final BEANTYPE model,
final MyEditCreatePopupShowCallback<BEANTYPE, STORETYPE, EDITORTYPE> callback)
{
this.store = store;
this.treeStore = null;
this.callback = callback;
initializeForEdition(model);
}
/**
* Show the Dialog box for edition of the given model
*
* @param treeStore
* the TreeStore
* @param model
* the model to be edited
* @param callback
* the {@link MyEditCreatePopupShowCallback} callback
*/
public void showForUpdate(final TreeStore<STORETYPE> treeStore, final BEANTYPE model,
final MyEditCreatePopupShowCallback<BEANTYPE, STORETYPE, EDITORTYPE> callback)
{
this.store = null;
this.treeStore = treeStore;
this.callback = callback;
initializeForEdition(model);
}
/**
* Called by {@link #showForUpdate(ListStore, AbstractDkBeanWithId)} after setting the store. Factor code for
* constructor with ListStore and TreeStore
*
* @param optionalArgs
*/
protected void initializeForEdition(final BEANTYPE model)
{
isCreation = false;
this.model = model;
this.newInstanceForCreation = null;
this.index = 0;
this.treeParentModel = null;
onBeforeShow();
super.show();
}
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
// SHOW FOR CREATION
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
/**
* Show the Dialog box for creation of a new model without store, so without possibility to add the created instance
* to any grid.
*/
public void showForCreation()
{
showForCreation((ListStore<STORETYPE>) null, null);
}
/**
* Show the Dialog box for creation of a new model without store, so without possibility to add the created instance
* to any grid.
*
* @param callback
* the {@link MyEditCreatePopupShowCallback} callback
*/
public void showForCreation(final MyEditCreatePopupShowCallback<BEANTYPE, STORETYPE, EDITORTYPE> callback)
{
showForCreation(null, callback);
}
/**
* Show the Dialog box for creation of a new model with no callback
*
* @param store
* the Store
*/
public void showForCreation(final ListStore<STORETYPE> store)
{
showForCreation(store, null);
}
/**
* Show the Dialog box for creation of a new model
*
* @param store
* the Store
* @param callback
* the {@link MyEditCreatePopupShowCallback} callback
*/
public void showForCreation(final ListStore<STORETYPE> store,
final MyEditCreatePopupShowCallback<BEANTYPE, STORETYPE, EDITORTYPE> callback)
{
this.store = store;
this.treeStore = null;
this.index = 0;
this.treeParentModel = null;
this.callback = callback;
initializeForCreation();
}
/**
* Show the Dialog box for creation of a new model with no save/show callback
*
* @param treeStore
* the TreeStore
* @param treeParent
* the parent model in which the created entry will be added
*/
public void showForCreation(final TreeStore<STORETYPE> treeStore, final STORETYPE treeParent)
{
showForCreation(treeStore, treeParent, null);
}
/**
* Show the Dialog box for creation of a new model
*
* @param treeStore
* the TreeStore
* @param treeParent
* the parent model in which the created entry will be added
* @param callback
* the {@link MyEditCreatePopupShowCallback} callback
*/
public void showForCreation(final TreeStore<STORETYPE> treeStore, final STORETYPE treeParent,
final MyEditCreatePopupShowCallback<BEANTYPE, STORETYPE, EDITORTYPE> callback)
{
showForCreation(treeStore, treeParent, 0, callback);
}
/**
* Show the Dialog box for creation of a new model with no save/show callback
*
* @param treeStore
* the TreeStore
* @param treeParent
* the parent model in which the created entry will be added
* @param index
* the index where to add the created element within parent
*/
public void showForCreation(final TreeStore<STORETYPE> treeStore, final STORETYPE treeParent, final int index)
{
showForCreation(treeStore, treeParent, index, null);
}
/**
* Show the Dialog box for creation of a new model
*
* @param treeStore
* the TreeStore
* @param treeParent
* the parent model in which the created entry will be added
* @param index
* the index where to add the created element within parent
* @param callback
* the {@link MyEditCreatePopupShowCallback} callback
*/
public void showForCreation(final TreeStore<STORETYPE> treeStore, final STORETYPE treeParent, final int index,
final MyEditCreatePopupShowCallback<BEANTYPE, STORETYPE, EDITORTYPE> callback)
{
this.store = null;
this.treeStore = treeStore;
this.index = index;
this.treeParentModel = treeParent;
this.callback = callback;
initializeForCreation();
}
/**
* Called by {@link #showForCreation(ListStore)} after setting the store. Factor code for constructor with ListStore
* and TreeStore
*
* @param optionalArgs
*/
protected void initializeForCreation()
{
isCreation = true;
final BEANTYPE newModelForCreation = getNewInstanceForCreation();
final BEANTYPE newBean = newModelForCreation == null ? createBeanInstance() : newModelForCreation;
if (newBean instanceof AbstractDkModifiableBeanWithId)
{
((AbstractDkModifiableBeanWithId) newBean).setCreation(true);
}
this.model = newBean;
this.index = 0;
onBeforeShow();
super.show();
}
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
// RPC
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
protected void callCreateOrUpdateRpc(final BEANTYPE bean, final BaseAsyncCallback<BEANTYPE> callback)
{
if (isCreation())
{
callCreateRpc(bean, callback);
}
else
{
callUpdateRpc(bean, callback);
}
}
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
// ABSTRACT METHODS
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
protected abstract BEANTYPE createBeanInstance();
protected abstract void callCreateRpc(BEANTYPE bean, BaseAsyncCallback<BEANTYPE> callback);
protected abstract void callUpdateRpc(BEANTYPE bean, BaseAsyncCallback<BEANTYPE> callback);
/**
* For more information about GWT Editor :<br>
* <a href="https://developers.google.com/web-toolkit/doc/latest/DevGuideUiEditors">
* https://developers.google.com/web-toolkit/doc/latest/DevGuideUiEditors</a>
*
* @return the editor driver
*/
protected abstract DkSimpleBeanEditorDriver<BEANTYPE, EDITORTYPE> getDriver();
/**
* May be overridden if you need an action to convert the result data (you can get it within you action using
* {@link BaseAction#getPreviousActionResult()}). This action will be added to the chain after save Action.
*
* @return null if not needed, an action otherwise.
*/
protected BaseAction<STORETYPE> getConvertResultAction()
{
return null;
}
/**
* May be overridden if you need an action to be executed right after saving (before
* {@link #getConvertResultAction()})
*
* @return a {@link BaseAction} or null
*/
protected BaseAction<BEANTYPE> getPostSaveAction()
{
return null;
}
/**
* Method to be overridden to provide possibility to add children of the modelToAdd
*
* @param treeStore
* the {@link TreeStore}
* @param parentModel
* the former parent model (may be the same or may have changed during edition)
* @param modelIndexInFormerParentModel
* the index where to add the model within the former parentModel (if -1 the add to the end) (not relevant
* if parent model has changed during update)
* @param modelToAdd
* the model to add
*/
protected void addModelToTreeStore(final TreeStore<STORETYPE> treeStore, final STORETYPE parentModel,
final int modelIndexInParent, final STORETYPE modelToAdd)
{
if (parentModel == null)
{
treeStore.add(modelToAdd);
}
else
{
if (modelIndexInParent != -1)
{
treeStore.insert(parentModel, modelIndexInParent, modelToAdd);
}
else
{
treeStore.add(parentModel, modelToAdd);
}
}
}
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
// PSEUDO EVENT MECHANISM
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
protected void onBeforeDriverEdit()
{
// Nothing done by default.
}
protected void doDriverEdit()
{
onBeforeDriverEdit();
setHeadingText(getLabelPopupTitle());
getSetupDriver().accept(new DkClearValidationVisitor());
if (callback != null)
{
callback.onBeforeDriverEdit(model, getSetupDriver());
}
getSetupDriver().edit(null);
getSetupDriver().edit(model);
if (callback != null)
{
callback.onAfterDriverEditBeforeShow(editor, getSetupDriver());
}
onAfterDriverEdit();
}
protected void onAfterDriverEdit()
{
// Nothing done by default.
}
protected void onBeforeShow()
{
doDriverEdit();
}
protected void onCancel()
{
// store.rejectChanges();
}
protected void onAfterSave(final STORETYPE storeTypeResult)
{
if (isCreation())
{
if (getStore() != null)
{
getStore().add(0, storeTypeResult);
}
else if (getTreeStore() != null)
{
if (treeParentModel == null)
{
getTreeStore().add(storeTypeResult);
}
else
{
addModelToTreeStore(getTreeStore(), treeParentModel, -1, storeTypeResult);
}
}
}
else
{
if (getStore() != null)
{
final int modelIndex = getStore().indexOf(storeTypeResult);
getStore().remove(modelIndex);
getStore().add(modelIndex, storeTypeResult);
}
else if (getTreeStore() != null)
{
final int modelIndexInFormerParentModel = getTreeStore().indexOf(storeTypeResult);
final STORETYPE formerParentModel = getTreeStore().getParent(storeTypeResult);
getTreeStore().remove(storeTypeResult);
final Couple<STORETYPE, Integer> couple = getNewParentModelAndIndexAfterSave(treeStore, formerParentModel,
modelIndexInFormerParentModel, storeTypeResult);
addModelToTreeStore(getTreeStore(), couple.first, couple.second, storeTypeResult);
}
}
if (callback != null)
{
callback.onAfterSave(storeTypeResult);
}
// store.commitChanges();
hide();
}
/**
* Override this method to calculate the new parent model and index within parent of the saved item (in case of a
* {@link TreeStore} ). By default this method returns the former parent model and former index. The saved item will
* be removed from former parent and added to the new one.
*
* @param treeStore
* the {@link TreeStore} in which the model has to be added updated save
* @param modelIndexInFormerParentModel
* the index of the model within the former parent model
* @param formerParentModel
* the former parent model of savedModel
* @param savedModel
* the just saved model
* @return a {@link Couple} in which first is the new parent model and second is the model index within former parent
* model
*/
protected Couple<STORETYPE, Integer> getNewParentModelAndIndexAfterSave(final TreeStore<STORETYPE> treeStore,
final STORETYPE formerParentModel, final int modelIndexInFormerParentModel, final STORETYPE savedModel)
{
return new Couple<STORETYPE, Integer>(formerParentModel, modelIndexInFormerParentModel);
}
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
// CALLBACKS FROM UI
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
@Override
protected void onButtonSaveClicked()
{
final Timer timer = new Timer()
{
@Override
public void run()
{
getSaveChainAction().execute();
}
};
timer.schedule(150);
}
protected BaseAction<?> getSaveChainAction()
{
final BaseAction<BEANTYPE> actionSave = new BaseAction<BEANTYPE>(getLabelActionSaveLoadingLabel())
{
@Override
protected void run()
{
getSetupDriver().accept(new DkPreFlushVisitor(true));
final BEANTYPE updatedModel = getSetupDriver().flush();
if (getSetupDriver().hasErrors())
{
MyMessageBox.alert(labelCorrectErrorsTitle, labelCorrectErrorsMessage);
stopChain();
onSuccess();
}
else
{
callCreateOrUpdateRpc(updatedModel, new BaseAsyncCallback<BEANTYPE>(this, true)
{
@Override
public void processSuccess(final BEANTYPE result)
{
// Nothing to be done. Process made in chain action.
}
});
}
}
};
final BaseAction<BEANTYPE> actionPostSave = getPostSaveAction();
if (actionPostSave != null)
{
actionSave.setLastChainAction(actionPostSave);
}
final BaseAction<STORETYPE> actionConvertResult = getConvertResultAction();
if (actionConvertResult != null)
{
actionSave.setLastChainAction(actionConvertResult);
}
final BaseAction<Void> actionAfterSave = new BaseAction<Void>(DkMain.i18n().action_update_screen_loading_label())
{
@Override
protected void run()
{
MyMessageBox.info(getLabelPopupSaveOkMessageBoxTitle(), getLabelPopupSaveOkMessageBoxMessage(),
new MyMessageBox.AfterCloseListener()
{
@SuppressWarnings("unchecked")
@Override
public void afterClose()
{
onAfterSave((STORETYPE) getPreviousActionResult());
}
});
onSuccess();
}
};
actionSave.setLastChainAction(actionAfterSave);
return actionSave;
}
@Override
protected void onButtonCancelClicked()
{
onCancel();
hide();
}
@Override
protected void onButtonResetClicked()
{
getSetupDriver().edit(model);
}
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
// LABELS ABSTRACT METHODS
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
@Override
protected abstract String getLabelPopupTitle();
protected abstract String getLabelActionSaveLoadingLabel();
protected abstract String getLabelPopupSaveOkMessageBoxTitle();
protected abstract String getLabelPopupSaveOkMessageBoxMessage();
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
// GETTERS / SETTERS
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
/**
* Create (the first time) and get the driver.
*
* @return the edition driver
*/
@SuppressWarnings("unchecked")
protected DkSimpleBeanEditorDriver<BEANTYPE, EDITORTYPE> getSetupDriver()
{
if (driver == null)
{
driver = getDriver();
driver.initialize(editor);
}
return driver;
}
/**
* @return the newInstanceForCreation
*/
public BEANTYPE getNewInstanceForCreation()
{
return newInstanceForCreation;
}
/**
* @param newInstanceForCreation
* the newInstanceForCreation to set
*/
public void setNewInstanceForCreation(final BEANTYPE newInstanceForCreation)
{
this.newInstanceForCreation = newInstanceForCreation;
}
/**
* @return the {@link ListStore}
*/
protected ListStore<STORETYPE> getStore()
{
return this.store;
}
/**
* @return the {@link TreeStore}
*/
protected TreeStore<STORETYPE> getTreeStore()
{
return this.treeStore;
}
/**
* @return whether current action is creation or edition
*/
protected boolean isCreation()
{
return isCreation;
}
/**
* @return the editor
*/
public EDITORTYPE getEditor()
{
return editor;
}
protected void setLabelCorrectErrorsTitle(final String labelCorrectErrorsTitle)
{
this.labelCorrectErrorsTitle = labelCorrectErrorsTitle;
}
protected void setLabelCorrectErrorsMessage(final String labelCorrectErrorsMessage)
{
this.labelCorrectErrorsMessage = labelCorrectErrorsMessage;
}
protected BEANTYPE getModel()
{
return model;
}
}