/*
* 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.widget;
import com.google.gwt.activity.shared.ActivityManager;
import com.google.gwt.core.client.Callback;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.place.shared.Place;
import com.google.gwt.place.shared.PlaceController;
import com.google.web.bindery.event.shared.SimpleEventBus;
import com.smartgwt.client.data.Criteria;
import com.smartgwt.client.data.DSCallback;
import com.smartgwt.client.data.DSRequest;
import com.smartgwt.client.data.DSResponse;
import com.smartgwt.client.data.DataSource;
import com.smartgwt.client.data.Record;
import com.smartgwt.client.data.RecordList;
import com.smartgwt.client.data.ResultSet;
import com.smartgwt.client.types.DSOperationType;
import com.smartgwt.client.types.SelectionStyle;
import com.smartgwt.client.util.BooleanCallback;
import com.smartgwt.client.util.EventHandler;
import com.smartgwt.client.widgets.Canvas;
import com.smartgwt.client.widgets.events.BrowserEvent;
import com.smartgwt.client.widgets.events.DropEvent;
import com.smartgwt.client.widgets.events.DropHandler;
import com.smartgwt.client.widgets.events.KeyPressEvent;
import com.smartgwt.client.widgets.events.KeyPressHandler;
import com.smartgwt.client.widgets.grid.ListGrid;
import com.smartgwt.client.widgets.grid.ListGridField;
import com.smartgwt.client.widgets.grid.ListGridRecord;
import com.smartgwt.client.widgets.grid.events.BodyKeyPressEvent;
import com.smartgwt.client.widgets.grid.events.BodyKeyPressHandler;
import com.smartgwt.client.widgets.grid.events.RecordClickEvent;
import com.smartgwt.client.widgets.grid.events.RecordClickHandler;
import com.smartgwt.client.widgets.grid.events.RecordDropEvent;
import com.smartgwt.client.widgets.grid.events.RecordDropHandler;
import com.smartgwt.client.widgets.grid.events.SelectionUpdatedEvent;
import com.smartgwt.client.widgets.grid.events.SelectionUpdatedHandler;
import com.smartgwt.client.widgets.layout.HLayout;
import com.smartgwt.client.widgets.layout.VLayout;
import com.smartgwt.client.widgets.menu.Menu;
import com.smartgwt.client.widgets.tile.TileGrid;
import com.smartgwt.client.widgets.toolbar.ToolStrip;
import com.smartgwt.client.widgets.viewer.DetailFormatter;
import com.smartgwt.client.widgets.viewer.DetailViewerField;
import cz.cas.lib.proarc.common.object.model.DatastreamEditorType;
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.Action;
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.DigitalObjectCopyMetadataAction;
import cz.cas.lib.proarc.webapp.client.action.DigitalObjectCopyMetadataAction.CopySelector;
import cz.cas.lib.proarc.webapp.client.action.DeleteAction;
import cz.cas.lib.proarc.webapp.client.action.DeleteAction.Deletable;
import cz.cas.lib.proarc.webapp.client.action.DigitalObjectFormValidateAction;
import cz.cas.lib.proarc.webapp.client.action.DigitalObjectFormValidateAction.ValidatableList;
import cz.cas.lib.proarc.webapp.client.action.FoxmlViewAction;
import cz.cas.lib.proarc.webapp.client.action.RefreshAction;
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.ImportBatchDataSource.BatchRecord;
import cz.cas.lib.proarc.webapp.client.ds.ImportBatchItemDataSource;
import cz.cas.lib.proarc.webapp.client.ds.MetaModelDataSource;
import cz.cas.lib.proarc.webapp.client.ds.RelationDataSource;
import cz.cas.lib.proarc.webapp.client.ds.RelationDataSource.RelationChangeEvent;
import cz.cas.lib.proarc.webapp.client.ds.RelationDataSource.RelationChangeHandler;
import cz.cas.lib.proarc.webapp.client.ds.RestConfig;
import cz.cas.lib.proarc.webapp.client.event.EditorLoadEvent;
import cz.cas.lib.proarc.webapp.client.event.EditorLoadHandler;
import cz.cas.lib.proarc.webapp.client.presenter.DigitalObjectEditing.DigitalObjectEditorPlace;
import cz.cas.lib.proarc.webapp.client.presenter.DigitalObjectEditor;
import cz.cas.lib.proarc.webapp.client.widget.DigitalObjectChildrenEditor.ChildActivities;
import cz.cas.lib.proarc.webapp.client.widget.DigitalObjectChildrenEditor.ChildEditorDisplay;
import java.util.logging.Logger;
/**
* Prepares items of a batch import. It involves meta data, item order, ...
*
* @author Jan Pokorsky
*/
public final class ImportBatchItemEditor extends HLayout implements Selectable<Record>,
RefreshAction.Refreshable, CopySelector {
private static final Logger LOG = Logger.getLogger(ImportBatchItemEditor.class.getName());
private final ClientMessages i18n;
private final ListGrid batchItemGrid;
private ListGridField fieldItemModel;
private final TileGrid thumbViewer;
private final MediaEditor digitalObjectPreview;
private boolean selectThumbInProgress = false;
private boolean selectListInProgress = false;
private BatchRecord batchRecord;
private FoxmlViewAction foxmlViewAction;
private DeleteAction deleteAction;
private SelectAction selectAllAction;
private DigitalObjectCopyMetadataAction copyMetadataAction;
private DigitalObjectFormValidateAction validateAction;
private final PlaceController childPlaces;
private final DigitalObjectEditor childEditor;
private final Canvas childDisplay;
private final ActionSource actionSource;
private final ReorderTask reorderTask = new ReorderTask();
private Action resumeAction;
private Handler handler;
public ImportBatchItemEditor(ClientMessages i18n) {
this.i18n = i18n;
this.setHeight100();
this.setWidth100();
this.actionSource = new ActionSource(this);
VLayout layout = new VLayout();
layout.setShowResizeBar(true);
layout.setResizeBarTarget("next");
batchItemGrid = createItemList();
layout.addMember(batchItemGrid);
// child editors
SimpleEventBus eventBus = new SimpleEventBus();
childPlaces = new PlaceController(eventBus);
childEditor = new DigitalObjectEditor(i18n, childPlaces, true);
childDisplay = initDigitalObjectEditor(childEditor, eventBus);
layout.addMember(childDisplay);
HLayout editorThumbLayout = new HLayout();
editorThumbLayout.setHeight100();
editorThumbLayout.addMember(layout);
thumbViewer = createThumbViewer();
editorThumbLayout.addMember(thumbViewer);
VLayout editorThumbToolbarLayout = new VLayout();
editorThumbToolbarLayout.setShowResizeBar(true);
editorThumbToolbarLayout.setResizeBarTarget("next");
createActions();
ToolStrip editorToolStrip = createEditorToolBar(actionSource);
editorThumbToolbarLayout.addMember(editorToolStrip);
editorThumbToolbarLayout.addMember(editorThumbLayout);
addMember(editorThumbToolbarLayout);
digitalObjectPreview = new MediaEditor(i18n);
digitalObjectPreview.addBackgroundColorListeners(thumbViewer);
digitalObjectPreview.setShowRefreshButton(true);
ToolStrip previewToolbar = Actions.createToolStrip();
previewToolbar.setMembers(digitalObjectPreview.getToolbarItems());
VLayout previewLayout = new VLayout();
previewLayout.setMembers(previewToolbar, digitalObjectPreview.getUI());
previewLayout.setWidth("40%");
previewLayout.setHeight100();
// previewLayout.setShowResizeBar(true);
// previewLayout.setResizeFrom("L");
addMember(previewLayout);
createEditorContextMenu(batchItemGrid.getContextMenu(), this);
createEditorContextMenu(thumbViewer.getContextMenu(), this);
}
public void setHandler(Handler handler) {
this.handler = handler;
}
public void onShow(BatchRecord batch) {
this.batchRecord = batch;
refresh();
}
public void onHide(BooleanCallback callback) {
DigitalObjectCopyMetadataAction.resetSelection();
callback.execute(true);
}
private Canvas initDigitalObjectEditor(DigitalObjectEditor childEditor, SimpleEventBus eventBus) {
childEditor.setImportView(true);
RelationDataSource relationDataSource = RelationDataSource.getInstance();
relationDataSource.addRelationChangeHandler(new RelationChangeHandler() {
@Override
public void onRelationChange(RelationChangeEvent event) {
// issue 262: isVisible seems to be always true and isAttached is always null.
// Add test isDrawn that seems to change for dettached widgets.
if (batchItemGrid.isVisible() && batchItemGrid.isDrawn()) {
updateCache();
}
}
});
ActivityManager activityManager = new ActivityManager(
new ChildActivities(childEditor), eventBus);
VLayout editorsLayout = new VLayout();
editorsLayout.addStyleName("defaultBorder");
activityManager.setDisplay(new ChildEditorDisplay(editorsLayout));
return editorsLayout;
}
private ListGrid createItemList() {
final ListGrid grid = new ListGrid() {
@Override
protected String getCellCSSText(ListGridRecord record, int rowNum, int colNum) {
// do not replace with hilites as they do not support UI refresh
if (DigitalObjectCopyMetadataAction.isSelectedCopyRecord(record)) {
return "color: #FF0000;";
} else {
return super.getCellCSSText(record, rowNum, colNum);
}
}
};
grid.setShowResizeBar(true);
grid.setSelectionType(SelectionStyle.MULTIPLE);
grid.setCanSort(false);
grid.setCanReorderRecords(true);
// disable autofit as it has rendering problems
// batchItemGrid.setAutoFitFieldWidths(true);
// batchItemGrid.setAutoFitWidthApproach(AutoFitWidthApproach.BOTH);
grid.setLeaveScrollbarGap(false);
grid.setDataSource(ImportBatchItemDataSource.getInstance());
fieldItemModel = new ListGridField(ImportBatchItemDataSource.FIELD_MODEL,
i18n.ImportBatchItemEditor_ListHeaderModel_Title());
fieldItemModel.setPrompt(i18n.ImportBatchItemEditor_ListHeaderModel_Hint());
fieldItemModel.setOptionDataSource(MetaModelDataSource.getInstance());
fieldItemModel.setValueField(MetaModelDataSource.FIELD_PID);
fieldItemModel.setDisplayField(MetaModelDataSource.FIELD_DISPLAY_NAME);
fieldItemModel.setAutoFetchDisplayMap(true);
fieldItemModel.setHidden(true);
ListGridField fieldPid = new ListGridField(ImportBatchItemDataSource.FIELD_PID,
i18n.ImportBatchItemEditor_ListHeaderPID_Title());
fieldPid.setPrompt(i18n.ImportBatchItemEditor_ListHeaderPID_Hint());
fieldPid.setHidden(true);
ListGridField fieldUser = new ListGridField(ImportBatchItemDataSource.FIELD_USER,
i18n.ImportBatchItemEditor_ListHeaderUser_Title());
fieldUser.setPrompt(i18n.ImportBatchItemEditor_ListHeaderUser_Hint());
fieldUser.setHidden(true);
ListGridField fieldFilename = new ListGridField(ImportBatchItemDataSource.FIELD_FILENAME,
i18n.ImportBatchItemEditor_ListHeaderFilename_Title());
fieldFilename.setPrompt(i18n.ImportBatchItemEditor_ListHeaderFilename_Hint());
ListGridField fieldPageIndex = new ListGridField(ImportBatchItemDataSource.FIELD_PAGE_INDEX,
i18n.ImportBatchItemEditor_ListHeaderPageIndex_Title());
fieldPageIndex.setPrompt(i18n.ImportBatchItemEditor_ListHeaderPageIndex_Hint());
fieldPageIndex.setHidden(true);
ListGridField fieldPageNumber = new ListGridField(ImportBatchItemDataSource.FIELD_PAGE_NUMBER,
i18n.ImportBatchItemEditor_ListHeaderPageNumber_Title());
fieldPageNumber.setPrompt(i18n.ImportBatchItemEditor_ListHeaderPageNumber_Hint());
ListGridField fieldPageType = new ListGridField(ImportBatchItemDataSource.FIELD_PAGE_TYPE_LABEL,
i18n.ImportBatchItemEditor_ListHeaderPageType_Title());
fieldPageType.setPrompt(i18n.ImportBatchItemEditor_ListHeaderPageType_Hint());
grid.setFields(fieldFilename, fieldPageNumber, fieldPageIndex, fieldPageType, fieldPid, fieldItemModel, fieldUser);
grid.setContextMenu(Actions.createMenu());
Actions.fixListGridContextMenu(grid);
grid.addBodyKeyPressHandler(new BodyKeyPressHandler() {
@Override
public void onBodyKeyPress(BodyKeyPressEvent event) {
selectAllAction.processEvent(event);
}
});
grid.addSelectionUpdatedHandler(new SelectionUpdatedHandler() {
@Override
public void onSelectionUpdated(SelectionUpdatedEvent event) {
if (selectListInProgress) {
selectListInProgress = false;
return ;
}
ListGridRecord[] selectedRecords = grid.getSelectedRecords();
thumbViewer.deselectAllRecords();
if (selectedRecords != null && selectedRecords.length == 1) {
// select thumbnail just in case of the single selection
int tileIndex = getThumbIndex(selectedRecords[0]);
// selectThumbInProgress = true;
// use record index instead of ListGridRecord to work around a smartgwt bug
thumbViewer.selectRecord(tileIndex);
ClientUtils.scrollToTile(thumbViewer, tileIndex);
} else if (selectedRecords != null && selectedRecords.length > 1) {
int[] indexes = new int[selectedRecords.length];
for (int i = 0; i < indexes.length; i++) {
indexes[i] = getThumbIndex(selectedRecords[i]);
}
thumbViewer.selectRecords(indexes);
}
selectBatchItem(true, selectedRecords);
}
});
grid.addRecordClickHandler(new RecordClickHandler() {
@Override
public void onRecordClick(RecordClickEvent event) {
// NOTE: RecordClickEvent is fired after SelectionUpdatedEvent!
// single selection is handled by onChildSelection
ListGridRecord[] selection = event.getViewer().getSelectedRecords();
if (selection.length < 2) {
return ;
}
Record record = event.getRecord();
// always preview last clicked record
previewItem(record);
}
});
grid.addRecordDropHandler(new RecordDropHandler() {
@Override
public void onRecordDrop(RecordDropEvent event) {
reorderTask.reorder(grid);
}
});
return grid;
}
@Override
public Record[] getSelection() {
if (reorderTask.isReordered()) {
return null;
}
return batchItemGrid.anySelected()
? batchItemGrid.getSelectedRecords()
: thumbViewer.getSelection();
}
@Override
public void refresh() {
// fetch models before digital objects
MetaModelDataSource.getModels(true, new Callback<ResultSet, Void>() {
@Override
public void onFailure(Void reason) {
}
@Override
public void onSuccess(ResultSet result) {
refreshData();
}
});
}
@Override
public void showCopySelection(Record[] records) {
if (records == null) {
return ;
}
RecordList copySelection = new RecordList(records);
for (int i = batchItemGrid.getRecords().length - 1; i >= 0; i--) {
Record item = batchItemGrid.getRecord(i);
DigitalObject listItem = DigitalObject.create(item);
Record select = copySelection.find(ImportBatchItemDataSource.FIELD_PID, listItem.getPid());
boolean refresh = false;
if (select != null) {
if (!DigitalObjectCopyMetadataAction.isSelectedCopyRecord(item)) {
DigitalObjectCopyMetadataAction.selectCopyRecord(item);
refresh = true;
}
} else {
if (DigitalObjectCopyMetadataAction.isSelectedCopyRecord(item)) {
DigitalObjectCopyMetadataAction.deselectCopyRecord(item);
refresh = true;
}
}
if (refresh) {
batchItemGrid.refreshRow(i);
}
}
}
private void refreshData() {
reorderTask.reset();
DigitalObjectCopyMetadataAction.resetSelection();
Criteria criteria = new Criteria(ImportBatchItemDataSource.FIELD_BATCHID, batchRecord.getId());
batchItemGrid.invalidateCache();
thumbViewer.setData(new Record[0]);
previewItem(null);
batchItemGrid.fetchData(criteria, new DSCallback() {
@Override
public void execute(DSResponse response, Object rawData, DSRequest request) {
if (RestConfig.isStatusOk(response)) {
Record[] data = response.getData();
Record[] copyRecords = batchItemGrid.getDataSource().copyRecords(data);
thumbViewer.setData(copyRecords);
batchItemGrid.selectSingleRecord(0);
batchItemGrid.focus();
ValidatableList.clearRowErrors(batchItemGrid);
}
}
});
}
/**
* As there is no API to reorder ListGrid records, it reloads data and
* tries to preserve selection of records.
*/
private void syncListWithTilesOnReorder() {
Record[] selection = thumbViewer.getSelection();
final int[] selectionIndex = new int[selection.length];
for (int i = 0; i < selection.length; i++) {
selectionIndex[i] = thumbViewer.getRecordIndex(selection[i]);
}
Criteria criteria = new Criteria(ImportBatchItemDataSource.FIELD_BATCHID, batchRecord.getId());
DigitalObjectCopyMetadataAction.resetSelection();
batchItemGrid.invalidateCache();
batchItemGrid.fetchData(criteria, new DSCallback() {
@Override
public void execute(DSResponse response, Object rawData, DSRequest request) {
if (RestConfig.isStatusOk(response)) {
batchItemGrid.selectRecords(selectionIndex);
}
}
});
}
/**
* Updates data source cache and attached data bounded components (ListGrid, TileGrid).
* It should be called when child editor changes affects import item descriptions
* or validation status.
*/
private void updateCache() {
final Record[] selections = getSelection();
Criteria criteria = new Criteria(ImportBatchItemDataSource.FIELD_BATCHID, batchRecord.getId());
if (selections.length == 0) {
return ;
} else if (selections.length == 1) {
// on single selection update only the involved row
DigitalObject dobj = DigitalObject.create(selections[0]);
criteria.addCriteria(ImportBatchItemDataSource.FIELD_PID, dobj.getPid());
}
final DataSource ds = batchItemGrid.getDataSource();
ds.fetchData(criteria, new DSCallback() {
@Override
public void execute(DSResponse response, Object rawData, DSRequest request) {
if (RestConfig.isStatusOk(response)) {
if (selections.length == 1) {
request.setOperationType(DSOperationType.UPDATE);
ds.updateCaches(response, request);
Record record = response.getData()[0];
Record copyRecord = ds.copyRecord(record);
int thumbIndex = getThumbIndex(copyRecord);
thumbViewer.getRecordList().set(thumbIndex, copyRecord);
selectNextChildAndFocus(record);
} else {
request.setOperationType(DSOperationType.UPDATE);
ds.updateCaches(response, request);
Record[] copyRecords = ds.copyRecords(response.getData());
thumbViewer.setData(copyRecords);
}
ValidatableList.clearRowErrors(batchItemGrid);
// refresh the copy selection as updated records are missing the copy attribute
showCopySelection(DigitalObjectCopyMetadataAction.getSelection());
}
}
});
}
private void selectNextChildAndFocus(Record record) {
int nextSelection = getNextSelection(record);
if (nextSelection < 0) {
return ;
}
final HandlerRegistration[] editorLoadHandler = new HandlerRegistration[1];
editorLoadHandler[0] = childEditor.addEditorLoadHandler(new EditorLoadHandler() {
@Override
public void onEditorLoad(EditorLoadEvent evt) {
editorLoadHandler[0].removeHandler();
Scheduler.get().scheduleDeferred(new ScheduledCommand() {
@Override
public void execute() {
childEditor.focus();
}
});
}
});
batchItemGrid.selectSingleRecord(nextSelection);
batchItemGrid.scrollToRow(nextSelection);
}
private TileGrid createThumbViewer() {
final TileGridEnhanced thumbGrid = new TileGridEnhanced();
thumbGrid.setBackgroundColor(DigitalObjectPreview.BACKGROUND_COLOR);
thumbGrid.setWidth(150);
thumbGrid.setHeight100();
thumbGrid.setMinWidth(150);
thumbGrid.setShowEdges(false);
thumbGrid.setCanReorderTiles(true);
thumbGrid.setWrapValues(true);
thumbGrid.setSelectionType(SelectionStyle.MULTIPLE);
// setTileProperties does not work; it replaces default renderer (smartgwt 2.5)
//thumbGrid.setTileProperties(tileCanvas);
// setDetailViewerProperties replaces default renderer and it is impossible to customize it not to show field titles (smartgwt 2.5)
//thumbGrid.setDetailViewerProperties(thumbViewer);
DetailViewerField dvfPageIndex = new DetailViewerField(ImportBatchItemDataSource.FIELD_PAGE_INDEX);
dvfPageIndex.setDetailFormatter(new DetailFormatter() {
@Override
public String format(Object value, Record record, DetailViewerField field) {
String number = record.getAttribute(ImportBatchItemDataSource.FIELD_PAGE_NUMBER);
String type = record.getAttribute(ImportBatchItemDataSource.FIELD_PAGE_TYPE_LABEL);
number = (number != null) ? number : "-";
value = (value != null) ? value : "-";
return ClientUtils.format("%s: %s<br>Index: %s", type, number, value);
}
});
final DetailViewerField dvfThumbnail = new DetailViewerField(ImportBatchItemDataSource.FIELD_THUMBNAIL);
dvfThumbnail.setImageURLPrefix(RestConfig.URL_DIGOBJECT_THUMBNAIL + "?");
dvfThumbnail.setType("image");
thumbGrid.setFields(dvfThumbnail, dvfPageIndex);
// TileLayoutPolicy.FLOW does not work as expected
// thumbGrid.setLayoutPolicy(TileLayoutPolicy.FLOW);
thumbGrid.setTileHeight(128 + 8 + 12 * 2);
thumbGrid.setTileWidth(120);
thumbGrid.setContextMenu(Actions.createMenu());
thumbGrid.addSelectionUpdatedHandler(new SelectionUpdatedHandler() {
@Override
public void onSelectionUpdated(SelectionUpdatedEvent event) {
if (selectThumbInProgress) {
selectThumbInProgress = false;
return ;
}
Record[] selection = thumbViewer.getSelection();
if (selection != null && selection.length == 1) {
// LOG.info("THUMB.onSelectionChanged.selection.state: " + event.getState() + ".attrs: " + Arrays.toString(selection[0].getAttributes()));
selectListInProgress = true;
int selectionIndex = batchItemGrid.getRecordIndex(selection[0]);
LOG.fine("thumb selects list: " + selectionIndex);
batchItemGrid.selectSingleRecord(selectionIndex);
batchItemGrid.scrollToRow(selectionIndex);
selectBatchItem(true, selection);
} else if (selection != null && selection.length > 1) {
int[] indexes = new int[selection.length];
for (int i = 0; i < indexes.length; i++) {
indexes[i] = batchItemGrid.getRecordIndex(selection[i]);
}
selectListInProgress = true;
batchItemGrid.deselectAllRecords();
selectListInProgress = true;
batchItemGrid.selectRecords(indexes);
selectBatchItem(false, selection);
}
actionSource.fireEvent();
// LOG.info("THUMB.onSelectionChanged.selection: " + Arrays.toString(selection));
}
});
thumbGrid.addRecordClickHandler(new com.smartgwt.client.widgets.tile.events.RecordClickHandler() {
@Override
public void onRecordClick(com.smartgwt.client.widgets.tile.events.RecordClickEvent event) {
// always preview last clicked record
LOG.fine("TG.onRecordClick");
previewItem(event.getRecord());
}
});
thumbGrid.addKeyPressHandler(new KeyPressHandler() {
@Override
public void onKeyPress(KeyPressEvent event) {
selectAllAction.processEvent(event);
}
});
thumbGrid.addDropHandler(new DropHandler() {
@Override
public void onDrop(DropEvent event) {
reorderTask.reorder(thumbGrid);
}
});
return thumbGrid;
}
private int getThumbIndex(Record r) {
int index = thumbViewer.getRecordList().findIndex(ImportBatchItemDataSource.FIELD_PID,
r.getAttribute(ImportBatchItemDataSource.FIELD_PID));
return index;
}
private void createActions() {
foxmlViewAction = new FoxmlViewAction(i18n);
deleteAction = new DeleteAction(new ItemDeletable(), i18n);
selectAllAction = new SelectAction();
resumeAction = new AbstractAction(
i18n.ImportBatchItemEditor_ActionResume_Title(),
"[SKIN]/actions/next.png",
i18n.ImportBatchItemEditor_ActionResume_Hint()) {
@Override
public void performAction(ActionEvent event) {
if (handler != null) {
handler.handleNextAction();
}
}
};
copyMetadataAction = new DigitalObjectCopyMetadataAction(i18n);
validateAction = new DigitalObjectFormValidateAction(i18n, new ValidatableList(batchItemGrid)) {
@Override
public boolean accept(ActionEvent event) {
return !reorderTask.isReordered();
}
};
}
private ToolStrip createEditorToolBar(ActionSource actionSource) {
ToolStrip toolbar = Actions.createToolStrip();
toolbar.addMember(Actions.asIconButton(new RefreshAction(i18n), this));
toolbar.addMember(Actions.asIconButton(selectAllAction, actionSource));
toolbar.addMember(Actions.asIconButton(foxmlViewAction, actionSource));
toolbar.addMember(Actions.asIconButton(deleteAction, actionSource));
toolbar.addMember(Actions.asIconButton(validateAction, actionSource));
toolbar.addMember(Actions.asIconButton(copyMetadataAction, actionSource));
toolbar.addMember(Actions.asIconButton(resumeAction, this));
Action saveAction = new SaveAction(i18n) {
@Override
public boolean accept(ActionEvent event) {
return reorderTask.isReordered();
}
@Override
public void performAction(ActionEvent event) {
reorderTask.save();
}
};
toolbar.addMember(Actions.asIconButton(saveAction, actionSource));
return toolbar;
}
private Menu createEditorContextMenu(Menu menu, Object contextSource) {
menu.addItem(Actions.asMenuItem(foxmlViewAction, contextSource, true));
menu.addItem(Actions.asMenuItem(deleteAction, contextSource, true));
menu.addItem(Actions.asMenuItem(copyMetadataAction, contextSource, false));
menu.addItem(Actions.asMenuItem(validateAction, contextSource, false));
return menu;
}
private int getNextSelection() {
ListGridRecord selectedRecord = batchItemGrid.getSelectedRecord();
return getNextSelection(selectedRecord);
}
private int getNextSelection(Record selectedRecord) {
RecordList rl = batchItemGrid.getRecordList();
int length = rl.getLength();
if (length == 0) {
return -1;
}
int nextSelectionIndex = 0;
if (selectedRecord != null) {
int recordIndex = batchItemGrid.getRecordIndex(selectedRecord);
int nextRecordIndex = recordIndex + 1;
if (nextRecordIndex >= length) {
// end of the list
nextRecordIndex = 0;
}
nextSelectionIndex = nextRecordIndex;
}
return nextSelectionIndex;
}
private void selectBatchItem(final boolean preview, final Record... selections) {
loadItemInChildEditor(selections);
if (!preview) {
return;
}
if (selections != null && selections.length == 1) {
previewItem(selections[0]);
return ;
}
previewItem(null);
}
/**
* Handles a new children selection.
*/
private void loadItemInChildEditor(Record[] records) {
actionSource.fireEvent();
if (records == null || records.length == 0 || reorderTask.isReordered()) {
childPlaces.goTo(Place.NOWHERE);
} else {
Place lastPlace = childPlaces.getWhere();
DatastreamEditorType lastEditorType = null;
if (lastPlace instanceof DigitalObjectEditorPlace) {
DigitalObjectEditorPlace lastDOEPlace = (DigitalObjectEditorPlace) lastPlace;
lastEditorType = lastDOEPlace.getEditorId();
}
lastEditorType = lastEditorType != null
? lastEditorType
: DatastreamEditorType.MODS;
childPlaces.goTo(new DigitalObjectEditorPlace(lastEditorType, records));
}
}
private void previewItem(Record r) {
DigitalObject dobj = r == null ? null : DigitalObject.createOrNull(r);
Canvas preview = digitalObjectPreview.getUI();
if (dobj != null) {
digitalObjectPreview.edit(dobj);
preview.show();
preview.getParentElement().enable();
} else {
preview.hide();
preview.getParentElement().disable();
}
}
private final class SelectAction extends AbstractAction {
SelectAction() {
super(i18n.ImportBatchItemEditor_ActionSaveAll_Title(),
"[SKIN]/actions/approve.png",
i18n.ImportBatchItemEditor_ActionSaveAll_Hint());
}
@Override
public boolean accept(ActionEvent event) {
return !reorderTask.isReordered();
}
@Override
public void performAction(ActionEvent event) {
Object source = event.getSource();
if (source instanceof ImportBatchItemEditor) {
selectAll((ImportBatchItemEditor) source);
}
}
public void selectAll(ImportBatchItemEditor editor) {
batchItemGrid.selectAllRecords();
}
/** Runs the action on CTRL+a */
public void processEvent(BodyKeyPressEvent event) {
if (!event.isCancelled()) {
if (processBrowserEvent(event)) {
event.cancel();
}
}
}
/** Runs the action on CTRL+a */
public void processEvent(KeyPressEvent event) {
if (!event.isCancelled()) {
if (processBrowserEvent(event)) {
event.cancel();
}
}
}
private boolean processBrowserEvent(BrowserEvent<?> event) {
if (event.isCtrlKeyDown()) {
if ("a".equals(EventHandler.getKeyEventCharacter())) {
selectAll(ImportBatchItemEditor.this);
return true;
}
}
return false;
}
}
/**
* Helper to reorder import items. Start with {@link #reorder(java.lang.Object) }.
*/
private final class ReorderTask {
private Object sourceWidget;
private Record[] originChildren;
/**
* Schedules save of reordered records. It expects to be invoked by
* DropHandlers when widgets still returns origin list of records.
*
* @param sourceWidget thumbs or item list
*/
public void reorder(Object sourceWidget) {
this.sourceWidget = sourceWidget;
originChildren = getRecords();
updateWidgetsOnReorder(sourceWidget, true);
}
public boolean isReordered() {
return originChildren != null;
}
private Record[] getRecords() {
Record[] records;
if (sourceWidget == batchItemGrid) {
records = batchItemGrid.getOriginalResultSet().toArray();
} else if (sourceWidget == thumbViewer) {
records = thumbViewer.getData();
} else {
throw new IllegalStateException("Unsupported widget: " + sourceWidget);
}
return records;
}
public void reset() {
originChildren = null;
sourceWidget = null;
thumbViewer.setShowEmptyMessage(true);
thumbViewer.setDisabled(false);
batchItemGrid.setShowEmptyMessage(true);
batchItemGrid.setDisabled(false);
childDisplay.setVisible(true);
}
private void updateWidgetsOnReorder(Object src, boolean reordering) {
if (src == batchItemGrid) {
thumbViewer.setShowEmptyMessage(!reordering);
thumbViewer.setData(new Record[0]);
thumbViewer.setDisabled(reordering);
} else if (src == thumbViewer) {
batchItemGrid.setShowEmptyMessage(!reordering);
batchItemGrid.setData(new Record[0]);
batchItemGrid.setDisabled(reordering);
}
childDisplay.setVisible(!reordering);
actionSource.fireEvent();
}
private void updateWidgetsOnSave(Object src) {
if (src == batchItemGrid) {
reset();
DataSource ds = batchItemGrid.getDataSource();
Record[] records = batchItemGrid.getOriginalResultSet().toArray();
Record[] copyRecords = ds.copyRecords(records);
thumbViewer.setData(copyRecords);
loadItemInChildEditor(batchItemGrid.getSelectedRecords());
actionSource.fireEvent();
} else if (src == thumbViewer) {
reset();
syncListWithTilesOnReorder();
actionSource.fireEvent();
} else {
refreshData();
}
}
public void save() {
if (originChildren == null) {
return ;
}
Record[] rs = getRecords();
if (RelationDataSource.equals(originChildren, rs)) {
updateWidgetsOnSave(sourceWidget);
return ;
}
String[] childPids = ClientUtils.toFieldValues(rs, RelationDataSource.FIELD_PID);
RelationDataSource relationDataSource = RelationDataSource.getInstance();
DigitalObject root = DigitalObject.create(batchRecord);
relationDataSource.reorderChildren(root, childPids, new BooleanCallback() {
@Override
public void execute(Boolean value) {
if (value != null && value) {
updateWidgetsOnSave(sourceWidget);
StatusView.getInstance().show(i18n.SaveAction_Done_Msg());
} else {
updateWidgetsOnSave(null);
}
}
});
}
}
public interface Handler {
void handleNextAction();
}
/**
* Deletes a list of import item records.
*/
private final class ItemDeletable implements Deletable {
public ItemDeletable() {
}
@Override
public void delete(Object[] items) {
if (items != null && items.length > 0) {
Record[] records = (Record[]) items;
String[] pids = ClientUtils.toFieldValues(records, ImportBatchItemDataSource.FIELD_PID);
String batchId = records[0].getAttribute(ImportBatchItemDataSource.FIELD_BATCHID);
ImportBatchItemDataSource.getInstance().delete(new Callback<Record[], String>() {
@Override
public void onFailure(String reason) {
}
@Override
public void onSuccess(Record[] result) {
DigitalObjectCopyMetadataAction.removeSelection(result);
RecordList rl = thumbViewer.getRecordList();
for (Record record : result) {
int index = rl.findIndex(ImportBatchItemDataSource.FIELD_PID,
record.getAttribute(ImportBatchItemDataSource.FIELD_PID));
if (index >= 0) {
rl.removeAt(index);
}
}
int visibleFirstRows = batchItemGrid.getVisibleRows()[0];
if (visibleFirstRows >= 0) {
ClientUtils.scrollToTile(thumbViewer, visibleFirstRows);
}
batchItemGrid.deselectAllRecords();
}
}, batchId, pids);
}
}
}
}