/* * 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.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.GwtEvent; import com.google.gwt.event.shared.HandlerManager; 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.Record; import com.smartgwt.client.data.RecordList; import com.smartgwt.client.data.ResultSet; import com.smartgwt.client.types.Alignment; import com.smartgwt.client.types.SelectionType; import com.smartgwt.client.types.VerticalAlignment; import com.smartgwt.client.util.SC; import com.smartgwt.client.widgets.Canvas; import com.smartgwt.client.widgets.IconButton; import com.smartgwt.client.widgets.Label; import com.smartgwt.client.widgets.events.DrawEvent; import com.smartgwt.client.widgets.events.DrawHandler; import com.smartgwt.client.widgets.events.VisibilityChangedEvent; import com.smartgwt.client.widgets.events.VisibilityChangedHandler; import com.smartgwt.client.widgets.layout.HLayout; import com.smartgwt.client.widgets.layout.Layout; 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.toolbar.ToolStrip; import com.smartgwt.client.widgets.toolbar.ToolStripSeparator; 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.ClientUtils.SweepTask; import cz.cas.lib.proarc.webapp.client.Editor; 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.DigitalObjectEditAction; import cz.cas.lib.proarc.webapp.client.action.DigitalObjectEditAction.AcceptFilter; import cz.cas.lib.proarc.webapp.client.action.DigitalObjectNavigateAction; import cz.cas.lib.proarc.webapp.client.action.DigitalObjectNavigateAction.ChildSelector; import cz.cas.lib.proarc.webapp.client.action.RefreshAction; import cz.cas.lib.proarc.webapp.client.action.RefreshAction.Refreshable; 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.MetaModelDataSource; import cz.cas.lib.proarc.webapp.client.ds.MetaModelDataSource.MetaModelRecord; import cz.cas.lib.proarc.webapp.client.ds.SearchDataSource; 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.event.HasEditorLoadHandlers; import cz.cas.lib.proarc.webapp.client.presenter.DigitalObjectEditing.DigitalObjectEditorPlace; import cz.cas.lib.proarc.webapp.client.widget.BatchDatastreamEditor; import cz.cas.lib.proarc.webapp.client.widget.DatastreamEditor; import cz.cas.lib.proarc.webapp.client.widget.DigitalObjectAdministrationEditor; import cz.cas.lib.proarc.webapp.client.widget.DigitalObjectChildrenEditor; import cz.cas.lib.proarc.webapp.client.widget.DigitalObjectChildrenEditor.ChildActivities; import cz.cas.lib.proarc.webapp.client.widget.DigitalObjectChildrenEditor.ChildEditorDisplay; import cz.cas.lib.proarc.webapp.client.widget.DigitalObjectParentEditor; import cz.cas.lib.proarc.webapp.client.widget.MediaEditor; import cz.cas.lib.proarc.webapp.client.widget.TextEditor; import java.util.Arrays; import java.util.EnumMap; import java.util.HashSet; import java.util.logging.Logger; /** * Edits digital object data streams. * * @author Jan Pokorsky */ public final class DigitalObjectEditor implements Refreshable, Selectable<Record>, HasEditorLoadHandlers, ChildSelector { private static final Logger LOG = Logger.getLogger(DigitalObjectEditor.class.getName()); private final ClientMessages i18n; private final VLayout widget; private final Label lblHeader; private final ToolStrip toolbar; private ToolStripSeparator customToolbarSeparator; private final VLayout editorContainer; private VLayout optionalEditorContainer; private EditorDescriptor currentEditor; private OptionalEditor optionalEditor; private IconButton optionalEditorSwitch; /** currently edited object {PID, MODEL_OBJECT}; should be replaced with some interface */ private Record[] selection; private final EnumMap<DatastreamEditorType, EditorDescriptor> editorCache; private final ActionSource actionSource; private final PlaceController places; private boolean importView; private boolean embeddedView; private boolean optionalView; private HandlerManager handlerManager; private Label unsupportedEditor; /** Holds the last used editor type. */ private DatastreamEditorType lastEditorType; public DigitalObjectEditor(ClientMessages i18n, PlaceController places) { this(i18n, places, false); } public DigitalObjectEditor(ClientMessages i18n, PlaceController places, boolean embedded) { this.i18n = i18n; this.places = places; this.editorCache = new EnumMap<DatastreamEditorType, EditorDescriptor>(DatastreamEditorType.class); this.widget = new VLayout(); this.lblHeader = new Label(); lblHeader.setAutoHeight(); lblHeader.setPadding(4); lblHeader.setStyleName(Editor.CSS_PANEL_DESCRIPTION_TITLE); this.actionSource = new ActionSource(this); this.embeddedView = embedded; this.toolbar = Actions.createToolStrip(); this.editorContainer = new VLayout(); editorContainer.setLayoutMargin(4); editorContainer.setWidth100(); editorContainer.setHeight100(); widget.addMember(lblHeader); widget.addMember(toolbar); if (embedded) { widget.addMember(editorContainer); } else { editorContainer.setResizeBarTarget("next"); HLayout multiView = new HLayout(); multiView.setWidth100(); multiView.setHeight100(); multiView.setLayoutMargin(4); multiView.addMember(editorContainer); initOptionalEditor(multiView); widget.addMember(multiView); } } private void initOptionalEditor(Layout multiView) { if (embeddedView) { return ; } editorContainer.setShowResizeBar(true); VLayout optionalEditorInnerContainer = new VLayout(); optionalEditorContainer = new VLayout(); optionalEditor = new OptionalEditor(i18n, optionalEditorInnerContainer); optionalEditorInnerContainer.addStyleName("defaultBorder"); optionalEditorContainer.setLayoutMargin(4); optionalEditorContainer.setWidth100(); optionalEditorContainer.setVisible(false); optionalEditorContainer.setMinMemberSize(200); optionalEditorContainer.setMembers(optionalEditorInnerContainer); multiView.addMember(optionalEditorContainer); optionalEditorContainer.addDrawHandler(new DrawHandler() { @Override public void onDraw(DrawEvent event) { // LOG.warning("onDraw: " + widget.getID() + ", drawn: " + optionalEditorContainer.isDrawn() // + ", isVisible: " + optionalEditorContainer.isVisible() // + ", isSelected: " + optionalEditorSwitch.isSelected() // ); if (optionalEditor.isEnabled()) { // Ignore events thrown while the editor is enabled as it breaks // the layout hierarchy in case browser history usage. // onDraw is necessary as VisibilityChangedEvent is not fired // before drawing a widget. return ; } switchOptionalEditor(true); } }); // switch the editor on/off by clicking the resize bar optionalEditorContainer.addVisibilityChangedHandler(new VisibilityChangedHandler() { @Override public void onVisibilityChanged(VisibilityChangedEvent event) { // LOG.warning("onVisibilityChanged: " + widget.getID() + ", visible: " + event.getIsVisible()); switchOptionalEditor(event.getIsVisible()); } }); } public Canvas getUI() { return widget; } /** * Shows UI for the passed editor type and fetches digital objects. * @param type optional type. If {@code null} the last type or a reasonable default is used * @param pids digital objects to fetch */ public void edit(DatastreamEditorType type, Record[] pids) { if (pids == null || pids.length == 0) { // this should occur just in case someone breakes URL in browser. notifyMissingPid(type); return ; } OpenEditorTask task = new OpenEditorTask(pids); edit(type, task, pids.length > 1); } /** * {@link #edit(cz.cas.lib.proarc.common.object.model.DatastreamEditorType, com.smartgwt.client.data.Record[]) * description} */ public void edit(DatastreamEditorType type, String... pids) { if (pids.length == 0) { // this should occur just in case someone breakes URL in browser. notifyMissingPid(type); return ; } OpenEditorTask task = new OpenEditorTask(pids); edit(type, task, pids.length > 1); } private void notifyMissingPid(DatastreamEditorType type) { ClientUtils.severe(LOG, "invalid edit parameters: %s, no pid", type); SC.warn("Invalid URL!"); places.goTo(Place.NOWHERE); } private void edit(DatastreamEditorType type, OpenEditorTask task, boolean multiselection) { this.selection = null; if (type == null) { // ClientUtils.warning(LOG, "missing type, objects: %s", task.toString()); // reasonable default if (lastEditorType != null) { type = lastEditorType; } else if (optionalView) { type = multiselection ? DatastreamEditorType.PARENT : DatastreamEditorType.MEDIA; } else { type = multiselection ? DatastreamEditorType.PARENT : DatastreamEditorType.MODS; } } lastEditorType = type; EditorDescriptor previousEditor = currentEditor; currentEditor = getDatastreamEditor(type); updateToolbar(previousEditor, currentEditor); task.start(); } public void focus() { if (currentEditor != null) { currentEditor.getEditor().focus(); } } private void setSelection(Record[] records) { this.selection = records; actionSource.fireEvent(); } private void openEditor() { Scheduler.get().scheduleDeferred(new ScheduledCommand() { @Override public void execute() { openEditorImpl(); } }); } private void openEditorImpl() { final DatastreamEditor editor = currentEditor.getEditor(); final Record[] records = getSelection(); DigitalObject[] dobjs = records == null ? new DigitalObject[0] : DigitalObject.toArray(records); if (dobjs.length > 1) { setDescription(currentEditor.getTitle(), i18n.DigitalObjectEditor_MultiSelection_Title(String.valueOf(dobjs.length)), null); BatchDatastreamEditor beditor = editor.getCapability(BatchDatastreamEditor.class); if (beditor != null) { beditor.edit(DigitalObject.toArray(records)); ClientUtils.setMembers(editorContainer, editor.getUI()); } else { openUnsupportedEditor(); } } else { MetaModelRecord model = dobjs[0].getModel(); setDescription(currentEditor.getTitle(), getLabel(records[0]), model); if (model.isSupportedDatastream(currentEditor.getType().name())) { editor.edit(dobjs[0]); ClientUtils.setMembers(editorContainer, editor.getUI()); } else { openUnsupportedEditor(); } } openOptionalEditor(dobjs); // editorContainer.show(); } private void openOptionalEditor(DigitalObject... dobj) { if (optionalEditor == null) { return ; } if (currentEditor != null && currentEditor.getType() == DatastreamEditorType.CHILDREN) { DigitalObject[] children = DigitalObject.toArray(getChildSelection()); optionalEditor.open(children); } else { optionalEditor.open(dobj); } } private void openUnsupportedEditor() { if (currentEditor != null) { updateToolbar(currentEditor, null); currentEditor = null; } editorContainer.setMembers(getUnsupportedEditor()); } private Label getUnsupportedEditor() { if (unsupportedEditor == null) { unsupportedEditor = new Label(); unsupportedEditor.setContents(i18n.DigitalObjectEditor_UnsupportedEditor_Msg()); unsupportedEditor.setIcon("[SKIN]/Dialog/warn.png"); unsupportedEditor.setIconSize(2 * 16); unsupportedEditor.setLayoutAlign(Alignment.CENTER); unsupportedEditor.setLayoutAlign(VerticalAlignment.CENTER); unsupportedEditor.setHeight100(); unsupportedEditor.setWidth100(); unsupportedEditor.setAlign(Alignment.CENTER); } return unsupportedEditor; } @Override public void refresh() { if (currentEditor == null) { return ; } Refreshable r = currentEditor.getEditor().getCapability(Refreshable.class); if (r != null) { r.refresh(); } } @Override public Record[] getSelection() { return selection; } @Override public Record[] getChildSelection() { Record[] result = null; if (currentEditor != null) { ChildSelector selector = currentEditor.getEditor().getCapability(ChildSelector.class); if (selector != null) { result = selector.getChildSelection(); } } return result; } private void updateToolbar(EditorDescriptor oldEditor, EditorDescriptor newEditor) { if (customToolbarSeparator == null) { initToolbar(toolbar, actionSource); } if (oldEditor == newEditor) { return ; } if (oldEditor != null) { toolbar.removeMembers(oldEditor.getToolbarItems()); } Canvas[] customToolbar = newEditor == null ? new Canvas[0] : newEditor.getToolbarItems(); if (customToolbar.length > 0 && !(customToolbar[0] instanceof ToolStripSeparator)) { customToolbarSeparator.setVisible(true); } else { customToolbarSeparator.setVisible(false); } int addCustomIdx = toolbar.getMemberNumber(customToolbarSeparator); for (Canvas item : customToolbar) { toolbar.addMember(item, ++addCustomIdx); } } private ToolStrip initToolbar(ToolStrip t, ActionSource source) { RefreshAction refreshAction = new RefreshAction(i18n); DigitalObjectEditAction modsEditAction = new DigitalObjectEditAction( i18n.ImportBatchItemEditor_TabMods_Title(), i18n.DigitalObjectEditAction_Hint(), null, DatastreamEditorType.MODS, embeddedView ? new AcceptFilter(true, true) : new AcceptFilter(false, false), places); DigitalObjectEditAction ocrEditAction = new DigitalObjectEditAction( i18n.ImportBatchItemEditor_TabOcr_Title(), i18n.DigitalObjectEditAction_Hint(), null, DatastreamEditorType.OCR, places); DigitalObjectEditAction noteEditAction = new DigitalObjectEditAction( i18n.ImportBatchItemEditor_TabNote_Title(), i18n.ImportBatchItemEditor_TabNote_Hint(), null, DatastreamEditorType.NOTE, places); DigitalObjectEditAction parentEditAction = new DigitalObjectEditAction( i18n.DigitalObjectEditor_ParentAction_Title(), i18n.DigitalObjectEditAction_Hint(), null, DatastreamEditorType.PARENT, // support multiple selection just in case DigitalObjectEditor is embeded embeddedView ? new AcceptFilter(true, true) : new AcceptFilter(false, false), places); DigitalObjectEditAction mediaEditAction = new DigitalObjectEditAction( i18n.DigitalObjectEditor_MediaAction_Title(), i18n.DigitalObjectEditor_MediaAction_Hint(), null, DatastreamEditorType.MEDIA, places); DigitalObjectEditAction childrenEditAction = new DigitalObjectEditAction( i18n.DigitalObjectEditor_ChildrenAction_Title(), i18n.DigitalObjectEditor_ChildrenAction_Hint(), null, DatastreamEditorType.CHILDREN, places); DigitalObjectEditAction atmEditAction = new DigitalObjectEditAction( i18n.DigitalObjectEditor_AdministrationAction_Title(), i18n.DigitalObjectEditor_AdministrationAction_Hint(), null, DatastreamEditorType.ATM, new AcceptFilter(true, true), places); if (embeddedView) { Action actionMore = Actions.emptyAction(i18n.ActionsMenu_Title(), null, null); IconMenuButton actionsMenu = Actions.asIconMenuButton(actionMore, source); t.addMember(actionsMenu); Menu menu = Actions.createMenu(); menu.addItem(Actions.asMenuItem(refreshAction, source, false)); // if (!optionalView) { menu.addItem(Actions.asMenuItem(modsEditAction, source, false)); // } menu.addItem(Actions.asMenuItem(noteEditAction, source, false)); if (!importView/* && !optionalView*/) { menu.addItem(Actions.asMenuItem(parentEditAction, source, false)); } if (!importView) { menu.addItem(Actions.asMenuItem(mediaEditAction, source, false)); } menu.addItem(Actions.asMenuItem(ocrEditAction, source, false)); menu.addItem(Actions.asMenuItem(atmEditAction, source, false)); actionsMenu.setMenu(menu); } else { t.addMember(Actions.asIconButton(refreshAction, source)); Action actionEditors = Actions.emptyAction(i18n.EditorsAction_Title(), "[SKIN]/actions/edit.png", i18n.EditorsAction_Hint()); IconMenuButton btnEditors = Actions.asIconMenuButton(actionEditors, source); Menu menuEditors = Actions.createMenu(); menuEditors.addItem(Actions.asMenuItem(modsEditAction, source, false)); menuEditors.addItem(Actions.asMenuItem(noteEditAction, source, false)); menuEditors.addItem(Actions.asMenuItem(parentEditAction, source, false)); menuEditors.addItem(Actions.asMenuItem(mediaEditAction, source, false)); menuEditors.addItem(Actions.asMenuItem(ocrEditAction, source, false)); menuEditors.addItem(Actions.asMenuItem(childrenEditAction, source, false)); menuEditors.addItem(Actions.asMenuItem(atmEditAction, source, false)); btnEditors.setMenu(menuEditors); t.addMember(btnEditors); DigitalObjectNavigateAction parentAction = DigitalObjectNavigateAction.parent(i18n, places); DigitalObjectNavigateAction childAction = DigitalObjectNavigateAction.child(i18n, places); DigitalObjectNavigateAction prevSiblingAction = DigitalObjectNavigateAction.previous(i18n, places); DigitalObjectNavigateAction nextSiblingAction = DigitalObjectNavigateAction.next(i18n, places); t.addMember(Actions.asIconButton(parentAction, source)); t.addMember(Actions.asIconButton(childAction, source)); t.addMember(Actions.asIconButton(prevSiblingAction, source)); t.addMember(Actions.asIconButton(nextSiblingAction, source)); } customToolbarSeparator = new ToolStripSeparator(); customToolbarSeparator.setVisible(false); t.addMember(customToolbarSeparator); if (!embeddedView) { t.addFill(); optionalEditorSwitch = Actions.asIconButton(new SwitchOptionalEditorAction(), source); optionalEditorSwitch.setSelected(false); optionalEditorSwitch.setActionType(SelectionType.CHECKBOX); optionalEditorSwitch.setShowSelectedIcon(true); t.addMember(optionalEditorSwitch); } return t; } private void switchOptionalEditor(boolean state) { if (optionalEditor == null || state == optionalEditor.isEnabled()) { return ; } optionalEditorSwitch.setSelected(state); optionalEditor.setEnabled(optionalEditorSwitch.getSelected()); if (state) { DigitalObject[] selection = DigitalObject.toArray(getSelection()); openOptionalEditor(selection); } } /** * Use to customize editor for batch import items. */ public void setImportView(boolean importView) { this.importView = importView; } /** * Use to customize editor as an embedded view. */ public void setEmbeddedView(boolean embeddedView) { this.embeddedView = embeddedView; } /** * Use to customize editor as an optional view. */ public void setOptionalView(boolean optionalView) { this.optionalView = optionalView; } private EditorDescriptor getDatastreamEditor(DatastreamEditorType type) { EditorDescriptor desc = editorCache.get(type); if (desc != null) { return desc; } DatastreamEditor deditor = null; String title = ""; switch (type) { case OCR: title = i18n.ImportBatchItemEditor_TabOcr_Title(); deditor = TextEditor.ocr(i18n); break; case NOTE: title = i18n.ImportBatchItemEditor_TabNote_Title(); deditor = TextEditor.note(i18n); break; case MEDIA: title = i18n.DigitalObjectEditor_MediaEditor_Title(); deditor = new MediaEditor(i18n); break; case MODS: title = i18n.ImportBatchItemEditor_TabMods_Title(); deditor = new ModsMultiEditor(i18n); break; case PARENT: title = i18n.DigitalObjectEditor_ParentEditor_Title(); deditor = new DigitalObjectParentEditor(i18n); break; case CHILDREN: title = i18n.DigitalObjectEditor_ChildrenEditor_Title(); deditor = new DigitalObjectChildrenEditor(i18n, places, optionalEditor); break; case ATM: title = i18n.DigitalObjectEditor_AdministrationEditor_Title(); deditor = new DigitalObjectAdministrationEditor(i18n); break; } title = ClientUtils.format("<b>%s</b>", title); desc = new EditorDescriptor(deditor, title, type); editorCache.put(type, desc); attachDatastreamEditor(deditor); return desc; } /** * Forwards editor events. */ private void attachDatastreamEditor(DatastreamEditor deditor) { if (deditor instanceof HasEditorLoadHandlers) { ((HasEditorLoadHandlers) deditor).addEditorLoadHandler(new EditorLoadHandler() { @Override public void onEditorLoad(EditorLoadEvent evt) { fireEvent(evt); } }); } } private void setDescription(String editorTitle, String objectLabel, MetaModelRecord mr) { String content; if (mr != null) { // Editor Name - Model - Label String model = mr.getDisplayName(); content = ClientUtils.format("%s - %s: %s", editorTitle, model, objectLabel); } else { // Editor Name - Label content = ClientUtils.format("%s: %s", editorTitle, objectLabel); } lblHeader.setContents(content); } private String getLabel(Record r) { return r == null ? "[ERROR]" : r.getAttribute(SearchDataSource.FIELD_LABEL); } @Override public HandlerRegistration addEditorLoadHandler(EditorLoadHandler handler) { return ensureHandlers().addHandler(EditorLoadEvent.TYPE, handler); } @Override public void fireEvent(GwtEvent<?> event) { if (handlerManager != null) { handlerManager.fireEvent(event); } } private HandlerManager ensureHandlers() { return handlerManager == null ? handlerManager = createHandlerManager() : handlerManager; } private HandlerManager createHandlerManager() { return new HandlerManager(this); } /** Holds already created editor and its toolbar */ private static final class EditorDescriptor { private final DatastreamEditor editor; private final Canvas[] toolbarItems; private final String title; private final DatastreamEditorType type; EditorDescriptor(DatastreamEditor editor, String title, DatastreamEditorType type) { this.editor = editor; toolbarItems = editor.getToolbarItems(); this.title = title; this.type = type; } public DatastreamEditor getEditor() { return editor; } public DatastreamEditorType getType() { return type; } public String getTitle() { return title; } public Canvas[] getToolbarItems() { return toolbarItems; } } /** * Opens editor when object description and model object are fetched. */ private final class OpenEditorTask extends SweepTask implements Callback<ResultSet, Void> { private RecordList searchList; private ResultSet modelResultSet; private final String[] pids; private final Record[] digitalObjects; /** * It will fetch model and other attributes for each PID. */ public OpenEditorTask(String[] pids) { this(null, pids); } /** * No fetch. It will use records as digital objects. */ public OpenEditorTask(Record[] digitalObjects) { this(digitalObjects, null); } private OpenEditorTask(Record[] digitalObjects, String[] pids) { this.pids = pids; this.digitalObjects = digitalObjects; } public void start() { expect(); if (pids != null) { initSearchList(pids); } initModels(); release(); } private void initModels() { expect(); MetaModelDataSource.getModels(false, this); } private void initSearchList(String[] pids) { expect(); SearchDataSource.getInstance().find(pids, new Callback<ResultSet, Void>() { @Override public void onFailure(Void reason) { searchList = new RecordList(); release(); } @Override public void onSuccess(ResultSet result) { searchList = result; release(); } }); } /** * Collects responses and opens editor. */ @Override protected void processing() { Record[] records = processRecords(); if (records == null) { return ; } setSelection(records); openEditor(); } private Record[] processRecords() { Record[] records; if (pids != null) { records = searchList.toArray(); String error = checkSearchedRecordsConsistency(records); if (error != null) { SC.warn(error); places.goTo(Place.NOWHERE); return null; } } else { records = digitalObjects; } return records; } private String checkSearchedRecordsConsistency(Record[] records) { String error = null; HashSet<String> pidSet = new HashSet<String>(Arrays.asList(pids)); for (Record record : records) { String recordPid = record.getAttribute(SearchDataSource.FIELD_PID); if (!pidSet.remove(recordPid)) { error = ClientUtils.format("PID %s not requested!", recordPid); break; } else if (SearchDataSource.isDeleted(record)) { error = ClientUtils.format("PID %s is deleted!", recordPid); break; } } if (error == null && !pidSet.isEmpty()) { error = ClientUtils.format("PID %s not found!", pidSet.toString()); } return error; } @Override public void onFailure(Void reason) { modelResultSet = new ResultSet(); release(); } @Override public void onSuccess(ResultSet result) { modelResultSet = result; release(); } @Override public String toString() { StringBuilder sb = new StringBuilder(); if (pids != null) { sb.append("pids: ").append(Arrays.toString(pids)); } else { String[] recordPids = ClientUtils.toFieldValues(digitalObjects, SearchDataSource.FIELD_PID); sb.append("records: ").append(Arrays.toString(recordPids)); } return super.toString(); } } public static final class OptionalEditor { private final PlaceController embeddedPlaces; private boolean editorEnabled; private OptionalEditor(ClientMessages i18n, Layout previewContainer) { SimpleEventBus eventBus = new SimpleEventBus(); embeddedPlaces = new PlaceController(eventBus); DigitalObjectEditor embeddedEditor = new DigitalObjectEditor(i18n, embeddedPlaces, true); embeddedEditor.setOptionalView(true); ActivityManager activityManager = new ActivityManager( new ChildActivities(embeddedEditor), eventBus); activityManager.setDisplay(new ChildEditorDisplay(previewContainer)); } public void open(DigitalObject... objects) { open(null, objects); } public void open(DatastreamEditorType editor, DigitalObject... objects) { if (!editorEnabled) { return ; } DigitalObject openObject = findRecentSelection(objects); if (openObject == null) { embeddedPlaces.goTo(Place.NOWHERE); return ; } // LOG.log(Level.SEVERE, "# openOptionalEditor: " + objects.length, new IllegalStateException(openObject.toString())); embeddedPlaces.goTo(new DigitalObjectEditorPlace(editor, openObject)); } private DigitalObject findRecentSelection(DigitalObject... objects) { if (objects == null || objects.length == 0 || objects[0] == null) { return null; } else if (objects.length == 1) { return objects[0]; } for (DigitalObject object : objects) { Record record = object.getRecord(); Boolean isLastSelection = record.getAttributeAsBoolean(DigitalObjectChildrenEditor.LAST_CLICKED_ATTR); if (isLastSelection != null && isLastSelection) { return object; } } return null; } public boolean isEnabled() { return editorEnabled; } public void setEnabled(boolean editorEnabled) { this.editorEnabled = editorEnabled; } } private final class SwitchOptionalEditorAction extends AbstractAction { public SwitchOptionalEditorAction() { super(i18n.DigitalObjectEditor_OptionalEditorAction_Title(), null, i18n.DigitalObjectEditor_OptionalEditorAction_Hint()); } @Override public void performAction(ActionEvent event) { boolean visible = optionalEditorContainer.isVisible(); // fires VisibilityChangedEvent // see optionalEditorContainer.addVisibilityChangedHandler in initOptionalEditor optionalEditorContainer.setVisible(!visible); } } }