/*
* Copyright (C) 2012 Jan Pokorsky
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package cz.cas.lib.proarc.webapp.client.presenter;
import com.google.gwt.event.shared.HandlerRegistration;
import com.smartgwt.client.util.BooleanCallback;
import com.smartgwt.client.util.Page;
import com.smartgwt.client.widgets.Canvas;
import com.smartgwt.client.widgets.form.events.HasSubmitValuesHandlers;
import com.smartgwt.client.widgets.form.events.SubmitValuesEvent;
import com.smartgwt.client.widgets.form.events.SubmitValuesHandler;
import com.smartgwt.client.widgets.layout.VLayout;
import com.smartgwt.client.widgets.menu.IconMenuButton;
import com.smartgwt.client.widgets.menu.Menu;
import com.smartgwt.client.widgets.menu.MenuItem;
import cz.cas.lib.proarc.webapp.client.ClientMessages;
import cz.cas.lib.proarc.webapp.client.ClientUtils;
import cz.cas.lib.proarc.webapp.client.action.AbstractAction;
import cz.cas.lib.proarc.webapp.client.action.ActionEvent;
import cz.cas.lib.proarc.webapp.client.action.Actions;
import cz.cas.lib.proarc.webapp.client.action.Actions.ActionSource;
import cz.cas.lib.proarc.webapp.client.action.RefreshAction.Refreshable;
import cz.cas.lib.proarc.webapp.client.action.SaveAction;
import cz.cas.lib.proarc.webapp.client.action.Selectable;
import cz.cas.lib.proarc.webapp.client.ds.DigitalObjectDataSource.DigitalObject;
import cz.cas.lib.proarc.webapp.client.ds.ModsCustomDataSource;
import cz.cas.lib.proarc.webapp.client.ds.ModsCustomDataSource.DescriptionMetadata;
import cz.cas.lib.proarc.webapp.client.ds.ModsCustomDataSource.DescriptionSaveHandler;
import cz.cas.lib.proarc.webapp.client.ds.RelationDataSource;
import cz.cas.lib.proarc.webapp.client.event.EditorLoadEvent;
import cz.cas.lib.proarc.webapp.client.event.HasEditorLoadHandlers;
import cz.cas.lib.proarc.webapp.client.widget.AbstractDatastreamEditor;
import cz.cas.lib.proarc.webapp.client.widget.BatchDatastreamEditor;
import cz.cas.lib.proarc.webapp.client.widget.CatalogBrowser;
import cz.cas.lib.proarc.webapp.client.widget.DatastreamEditor;
import cz.cas.lib.proarc.webapp.client.widget.StatusView;
import java.util.HashSet;
import java.util.logging.Logger;
/**
* Edits MODS data in multiple custom editor (all fields, selected fields, plain XML).
*
* @author Jan Pokorsky
*/
// XXX rename to DescriptionMultiEditor
public final class ModsMultiEditor extends AbstractDatastreamEditor implements
BatchDatastreamEditor, Refreshable, Selectable<DigitalObject> {
private static final Logger LOG = Logger.getLogger(ModsMultiEditor.class.getName());
private static final HashSet<String> ACCEPT_BATCH_MODELS = new HashSet<String>();
static {
ACCEPT_BATCH_MODELS.add("model:page");
ACCEPT_BATCH_MODELS.add("model:oldprintpage");
}
private final VLayout uiContainer;
private final ModsCustomEditor modsCustomEditor;
private final ModsXmlEditor modsSourceEditor;
private final ModsBatchEditor modsBatchEditor;
private final CatalogBrowser catalogBrowser;
private DatastreamEditor activeEditor;
private final ClientMessages i18n;
private DigitalObject[] digitalObjects;
private MenuItem customEditorButton;
/**
* The simplified simple form. See issue 321.
*/
private MenuItem customEditorButton2;
private final ActionSource actionSource;
private HandlerRegistration submitCustomValuesRegistration;
private final SubmitValuesHandler submitCustomValuesHandler = new SubmitValuesHandler() {
@Override
public void onSubmitValues(SubmitValuesEvent event) {
save();
}
};
public ModsMultiEditor(ClientMessages i18n) {
this.i18n = i18n;
uiContainer = new VLayout();
modsCustomEditor = new ModsCustomEditor(i18n);
modsSourceEditor = new ModsXmlEditor(i18n);
modsBatchEditor = new ModsBatchEditor(i18n);
catalogBrowser = new CatalogBrowser(i18n);
catalogBrowser.setCompactUi(true);
actionSource = new ActionSource(this);
attachDatastreamEditor(modsCustomEditor);
}
/**
* Forwards editor events.
*/
private void attachDatastreamEditor(DatastreamEditor deditor) {
if (deditor instanceof HasEditorLoadHandlers) {
((HasEditorLoadHandlers) deditor).addEditorLoadHandler((EditorLoadEvent evt) -> {
fireEvent(evt);
});
}
}
@Override
public void edit(DigitalObject digitalObject) {
ClientUtils.fine(LOG, "edit %s", digitalObject);
DigitalObject[] dobjs = digitalObject == null ? null : new DigitalObject[] { digitalObject };
edit(dobjs);
}
@Override
public void edit(DigitalObject[] items) {
this.digitalObjects = items;
if (items == null || items.length == 0) {
// show nothing or throw exception!
} else if (items.length == 1) {
loadTabData(modsCustomEditor, items[0]);
} else {
String firstModelId = null;
boolean unsupportedBatch = false;
for (DigitalObject dobj : items) {
if (firstModelId == null) {
firstModelId = dobj.getModelId();
}
String currentModelId = dobj.getModelId();
if (!ACCEPT_BATCH_MODELS.contains(currentModelId) || !firstModelId.equals(currentModelId)) {
unsupportedBatch = true;
break;
}
}
if (unsupportedBatch) {
setActiveEditor(null);
} else {
loadBatch();
}
}
actionSource.fireEvent();
}
@Override
public void focus() {
if (activeEditor != null) {
activeEditor.focus();
}
}
public void save(BooleanCallback callback) {
callback = wrapSaveCallback(callback);
if (activeEditor == modsCustomEditor) {
saveCustomData(callback);
} else if (activeEditor == modsBatchEditor) {
saveBatchData(callback);
} else if (activeEditor == catalogBrowser) {
saveCatalogData(callback);
} else if (activeEditor == modsSourceEditor) {
saveXmlData(callback);
} else {
callback.execute(Boolean.TRUE);
}
}
private void save() {
save((Boolean value) -> {
if (value != null && value) {
StatusView.getInstance().show(i18n.SaveAction_Done_Msg());
}
});
}
/**
* Notifies other data sources to update its caches with object label.
*/
private BooleanCallback wrapSaveCallback(final BooleanCallback callback) {
BooleanCallback bc = (Boolean value) -> {
if (value != null && value) {
RelationDataSource.getInstance().fireRelationChange(digitalObjects[0].getPid());
}
callback.execute(value);
};
return bc;
}
@Override
@SuppressWarnings("unchecked")
public <T> T getCapability(Class<T> clazz) {
T c = null;
if (Refreshable.class.equals(clazz) || BatchDatastreamEditor.class.equals(clazz)) {
c = (T) this;
}
return c;
}
@Override
public Canvas[] getToolbarItems() {
SaveAction saveAction = new SaveAction(i18n) {
@Override
public void performAction(ActionEvent event) {
save();
}
@Override
public boolean accept(ActionEvent event) {
return activeEditor != null;
}
};
return new Canvas[] {
createModsMenu(),
Actions.asIconButton(saveAction, actionSource)
};
}
private IconMenuButton createModsMenu() {
IconMenuButton btnMods = Actions.asIconMenuButton(new AbstractAction(
i18n.ModsMultiEditor_ActionMods_Title(), "[SKIN]/actions/edit.png", null) {
@Override
public void performAction(ActionEvent event) {
}
@Override
public boolean accept(ActionEvent event) {
DigitalObject[] selections = getSelection();
return selections != null && selections.length == 1;
}
}, actionSource);
Menu menuMods = Actions.createMenu();
menuMods.addItem(Actions.asMenuItem(
new SwitchAction(modsCustomEditor,
i18n.ModsMultiEditor_TabSimple_Title(),
Page.getAppDir() + "images/silk/16/application_form_edit.png",
i18n.ModsMultiEditor_TabSimple_Hint()
) {
@Override
public void performAction(ActionEvent event) {
modsCustomEditor.setFormPrefix("");
super.performAction(event);
}
}, actionSource, false));
customEditorButton = menuMods.getItem(0);
menuMods.addItem(Actions.asMenuItem(
new SwitchAction(modsCustomEditor,
i18n.ModsMultiEditor_TabSimpleSimple_Title(),
Page.getAppDir() + "images/silk/16/application_form_edit.png",
i18n.ModsMultiEditor_TabSimpleSimple_Hint()
) {
@Override
boolean accept(DigitalObject obj) {
return "model:bdmarticle".equals(obj.getModelId());
}
@Override
public void performAction(ActionEvent event) {
modsCustomEditor.setFormPrefix("simple:");
super.performAction(event);
}
}, actionSource, false));
customEditorButton2 = menuMods.getItem(1);
menuMods.addItem(Actions.asMenuItem(
new SwitchAction(modsSourceEditor,
i18n.ModsMultiEditor_TabSource_Title(),
Page.getAppDir() + "images/oxygen/16/application_xml.png",
i18n.ModsMultiEditor_TabSource_Hint()
), actionSource, false));
menuMods.addItem(Actions.asMenuItem(
new SwitchAction(catalogBrowser,
i18n.ModsMultiEditor_TabCatalog_Title(),
"[SKIN]/DatabaseBrowser/data.png",
i18n.ModsMultiEditor_TabCatalog_Hint()
), actionSource, false));
btnMods.setMenu(menuMods);
return btnMods;
}
@Override
public Canvas getUI() {
return uiContainer;
}
@Override
public void refresh() {
if (activeEditor != null) {
Refreshable refreshable = activeEditor.getCapability(Refreshable.class);
if (refreshable != null) {
refreshable.refresh();
} else {
loadTabData(activeEditor, digitalObjects[0]);
}
}
}
@Override
public DigitalObject[] getSelection() {
return digitalObjects;
}
private void loadTabData(DatastreamEditor tab, DigitalObject digitalObject) {
setEnabledCustom2();
if (tab == modsCustomEditor) {
loadCustom(digitalObject);
} else {
setActiveEditor(tab);
tab.edit(digitalObject);
}
}
private void loadCustom(DigitalObject digitalObject) {
modsCustomEditor.edit(digitalObject);
if (modsCustomEditor.getCustomForm() != null) {
setActiveEditor(modsCustomEditor);
setEnabledCustom(true);
} else if (!modsCustomEditor.getFormPrefix().isEmpty()) {
modsCustomEditor.setFormPrefix("");
loadCustom(digitalObject);
} else {
// unknown model, use source form
setEnabledCustom(false);
loadTabData(modsSourceEditor, digitalObject);
}
}
private void loadBatch() {
if (activeEditor != modsBatchEditor) {
modsBatchEditor.refresh();
}
setActiveEditor(modsBatchEditor);
modsBatchEditor.edit(digitalObjects);
}
private void setActiveEditor(DatastreamEditor newEditor) {
if (newEditor != activeEditor) {
if (newEditor != null) {
uiContainer.setMembers(newEditor.getUI());
} else {
uiContainer.setMembers(new Canvas[0]);
}
if (submitCustomValuesRegistration != null) {
submitCustomValuesRegistration.removeHandler();
}
if (newEditor instanceof HasSubmitValuesHandlers) {
submitCustomValuesRegistration = ((HasSubmitValuesHandlers) newEditor)
.addSubmitValuesHandler(submitCustomValuesHandler);
}
activeEditor = newEditor;
}
}
private void setEnabledCustom(boolean enabled) {
if (customEditorButton != null) {
customEditorButton.setEnabled(enabled);
}
}
private void setEnabledCustom2() {
if (customEditorButton2 != null) {
DigitalObject[] selection = getSelection();
boolean enable = selection != null && selection.length == 1
&& "model:bdmarticle".equals(selection[0].getModelId());
customEditorButton2.setEnabled(enable);
}
}
private void saveCustomData(BooleanCallback callback) {
modsCustomEditor.save(callback);
}
private void saveXmlData(BooleanCallback callback) {
modsSourceEditor.save(callback, true, SaveAction.SaveValidation.RUN);
}
private void saveBatchData(BooleanCallback callback) {
modsBatchEditor.save(callback);
}
private void saveCatalogData(final BooleanCallback callback) {
String mods = catalogBrowser.getMods();
ModsCustomDataSource.getInstance().saveXmlDescription(digitalObjects[0], mods, new DescriptionSaveHandler() {
@Override
protected void onSave(DescriptionMetadata dm) {
super.onSave(dm);
callback.execute(Boolean.TRUE);
}
@Override
protected void onError() {
super.onError();
callback.execute(Boolean.FALSE);
}
});
}
private class SwitchAction extends AbstractAction {
private final DatastreamEditor tab;
private final String format;
public SwitchAction(DatastreamEditor tab, String title, String icon, String tooltip) {
this(tab, title, icon, tooltip, null);
}
public SwitchAction(DatastreamEditor tab, String title, String icon, String tooltip, String format) {
super(title, icon, tooltip);
this.tab = tab;
this.format = format;
}
@Override
public void performAction(ActionEvent event) {
loadTabData(tab, digitalObjects[0]);
}
@Override
public boolean accept(ActionEvent event) {
DigitalObject[] selections = getSelection();
return selections != null && selections.length == 1 && accept(selections[0]);
}
boolean accept(DigitalObject obj) {
return acceptFormat(obj);
}
boolean acceptFormat(DigitalObject obj) {
return format == null || format != null && format.equals(obj.getModel().getMetadataFormat());
}
}
}