package edu.ualberta.med.biobank.forms.linkassign; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import org.eclipse.core.databinding.observable.value.WritableValue; import org.eclipse.core.runtime.Status; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.osgi.util.NLS; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.BusyIndicator; import org.eclipse.swt.events.FocusAdapter; import org.eclipse.swt.events.FocusEvent; import org.eclipse.swt.events.ModifyEvent; import org.eclipse.swt.events.ModifyListener; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Label; import org.eclipse.ui.PlatformUI; import edu.ualberta.med.biobank.SessionManager; import edu.ualberta.med.biobank.common.action.Action; import edu.ualberta.med.biobank.common.action.scanprocess.CellInfo; import edu.ualberta.med.biobank.common.action.scanprocess.SpecimenHierarchyInfo; import edu.ualberta.med.biobank.common.action.scanprocess.SpecimenLinkProcessAction; import edu.ualberta.med.biobank.common.action.scanprocess.result.ProcessResult; import edu.ualberta.med.biobank.common.action.specimen.SpecimenLinkSaveAction; import edu.ualberta.med.biobank.common.action.specimen.SpecimenLinkSaveAction.AliquotedSpecimenInfo; import edu.ualberta.med.biobank.common.action.specimen.SpecimenLinkSaveAction.AliquotedSpecimenResInfo; import edu.ualberta.med.biobank.common.peer.SpecimenPeer; import edu.ualberta.med.biobank.common.util.RowColPos; import edu.ualberta.med.biobank.common.wrappers.ContainerLabelingSchemeWrapper; import edu.ualberta.med.biobank.common.wrappers.SpecimenTypeWrapper; import edu.ualberta.med.biobank.common.wrappers.SpecimenWrapper; import edu.ualberta.med.biobank.forms.linkassign.LinkFormPatientManagement.CEventComboCallback; import edu.ualberta.med.biobank.forms.linkassign.LinkFormPatientManagement.PatientTextCallback; import edu.ualberta.med.biobank.forms.listener.EnterKeyToNextFieldListener; import edu.ualberta.med.biobank.gui.common.BgcLogger; import edu.ualberta.med.biobank.gui.common.BgcPlugin; import edu.ualberta.med.biobank.gui.common.validators.NonEmptyStringValidator; import edu.ualberta.med.biobank.gui.common.widgets.BgcBaseText; import edu.ualberta.med.biobank.model.ActivityStatus; import edu.ualberta.med.biobank.validators.StringLengthValidator; import edu.ualberta.med.biobank.widgets.AliquotedSpecimenSelectionWidget; import edu.ualberta.med.biobank.widgets.grids.cell.PalletCell; import edu.ualberta.med.biobank.widgets.grids.cell.UICellStatus; import edu.ualberta.med.scannerconfig.dmscanlib.ScanCellPos; import gov.nih.nci.system.applicationservice.ApplicationException; // FIXME the custom selection is not done in this version. public class SpecimenLinkEntryForm extends AbstractLinkAssignEntryForm { public static final String ID = "edu.ualberta.med.biobank.forms.SpecimenLinkEntryForm"; protected static BgcLogger log = BgcLogger .getLogger(SpecimenLinkEntryForm.class.getName()); private static final String INVENTORY_ID_BINDING = "inventoryId-binding"; private static final String NEW_SINGLE_POSITION_BINDING = "newSinglePosition-binding"; private static BgcLogger logger = BgcLogger .getLogger(SpecimenLinkEntryForm.class.getName()); private static Mode mode = Mode.MULTIPLE; // TODO do not need a composite class anymore if only one link form is left private final LinkFormPatientManagement linkFormPatientManagement; // single linking // source specimen / type relation when only one specimen private AliquotedSpecimenSelectionWidget singleTypesWidget; protected boolean inventoryIdModified; private Label newSinglePositionLabel; private BgcBaseText newSinglePositionText; private StringLengthValidator newSinglePositionValidator; private boolean positionTextModified; // Multiple linking // list of source specimen / type widget for multiple linking private List<AliquotedSpecimenSelectionWidget> specimenTypesWidgets; private Composite multipleOptionsFields; // contains widgets to select type row by row private Composite typesSelectionPerRowComposite; // row:total (for one row number, associate the number of specimen found on // this row) private final Map<Integer, Integer> typesRows = new HashMap<Integer, Integer>(); // List of specimen types that a pallet can have. // private List<SpecimenTypeWrapper> palletSpecimenTypes; // source/type hierarchy selected (use rows order) private List<SpecimenHierarchyInfo> preSelections; public SpecimenLinkEntryForm() { linkFormPatientManagement = new LinkFormPatientManagement( widgetCreator, this); } @Override protected void init() throws Exception { log.debug("init"); super.init(); setPartName("Linking specimens"); setCanLaunchScan(true); } @Override protected String getFormTitle() { return "Linking specimens"; } @Override protected boolean isSingleMode() { log.debug("isSingleMode:" + mode.isSingleMode()); return mode.isSingleMode(); } @Override protected void setMode(Mode m) { log.debug("setMode: " + mode); mode = m; } @Override protected String getActivityTitle() { return "Specimen Link"; } @Override public BgcLogger getErrorLogger() { return logger; } @Override protected String getOkMessage() { return "Link specimens to their source specimens"; } @Override public String getNextOpenedFormId() { return ID; } @Override protected boolean showSinglePosition() { return true; } @Override protected void createCommonFields(Composite commonFieldsComposite) { // Patient number linkFormPatientManagement .createPatientNumberText(commonFieldsComposite); linkFormPatientManagement .setPatientTextCallback(new PatientTextCallback() { @Override public void focusLost() { setTypeCombos(); } @Override public void textModified() { } }); // Processing event and Collection events lists linkFormPatientManagement.createEventsWidgets(commonFieldsComposite); linkFormPatientManagement .setCEventComboCallback(new CEventComboCallback() { @Override public void selectionChanged() { setTypeCombos(); } }); } @Override protected void createMultipleFields(Composite parent) { multipleOptionsFields = toolkit.createComposite(parent); GridLayout layout = new GridLayout(3, false); layout.horizontalSpacing = 10; layout.marginWidth = 0; multipleOptionsFields.setLayout(layout); toolkit.paintBordersFor(multipleOptionsFields); GridData gd = new GridData(); gd.grabExcessHorizontalSpace = true; gd.horizontalAlignment = SWT.FILL; multipleOptionsFields.setLayoutData(gd); // plate selection createPlateToScanField(multipleOptionsFields); createScanButton(parent); // source/type hierarchy widgets typesSelectionPerRowComposite = toolkit.createComposite(parent); layout = new GridLayout(4, false); layout.horizontalSpacing = 10; typesSelectionPerRowComposite.setLayout(layout); toolkit.paintBordersFor(typesSelectionPerRowComposite); gd = new GridData(); gd.grabExcessHorizontalSpace = true; gd.horizontalAlignment = SWT.FILL; typesSelectionPerRowComposite.setLayoutData(gd); toolkit.createLabel(typesSelectionPerRowComposite, ""); toolkit.createLabel(typesSelectionPerRowComposite, "Source Specimen"); toolkit.createLabel(typesSelectionPerRowComposite, "Aliquoted Specimen Types"); toolkit.createLabel(typesSelectionPerRowComposite, ""); specimenTypesWidgets = new ArrayList<AliquotedSpecimenSelectionWidget>(); AliquotedSpecimenSelectionWidget precedent = null; for (int i = 0; i < ScanCellPos.ROW_MAX; i++) { final AliquotedSpecimenSelectionWidget typeWidget = new AliquotedSpecimenSelectionWidget( typesSelectionPerRowComposite, ContainerLabelingSchemeWrapper.SBS_ROW_LABELLING_PATTERN .charAt(i), widgetCreator, true); final int indexRow = i; typeWidget .addSelectionChangedListener(new ISelectionChangedListener() { @Override public void selectionChanged(SelectionChangedEvent event) { if (typeWidget.needToSave()) { SpecimenHierarchyInfo selection = typeWidget .getSelection(); if (selection != null) { @SuppressWarnings("unchecked") Map<RowColPos, PalletCell> cells = (Map<RowColPos, PalletCell>) palletWidget .getCells(); if (cells != null) { for (RowColPos rcp : cells.keySet()) { if (rcp.getRow() == indexRow) { PalletCell cell = cells.get(rcp); if (PalletCell.hasValue(cell)) { setHierarchyToCell(cell, selection); } } } palletWidget.redraw(); } } } if (palletWidget.isEverythingTyped()) { setDirty(true); } } }); typeWidget.addBindings(); specimenTypesWidgets.add(typeWidget); if (precedent != null) { precedent.setNextWidget(typeWidget); } precedent = typeWidget; typeWidget.setNumber(0); typeWidget.setEnabled(false); } } @Override protected void defaultInitialisation() { log.debug("defaultInitialisation"); super.defaultInitialisation(); setNeedSinglePosition(mode == Mode.SINGLE_POSITION); } @Override protected void setNeedSinglePosition(boolean position) { log.debug("setNeedSinglePosition: " + position); widgetCreator.setBinding(NEW_SINGLE_POSITION_BINDING, position); widgetCreator.showWidget(newSinglePositionLabel, position); widgetCreator.showWidget(newSinglePositionText, position); } @Override protected void createSingleFields(Composite parent) { Composite fieldsComposite = toolkit.createComposite(parent); GridLayout layout = new GridLayout(2, false); layout.horizontalSpacing = 10; fieldsComposite.setLayout(layout); toolkit.paintBordersFor(fieldsComposite); GridData gd = new GridData(); gd.horizontalSpan = 2; gd.grabExcessHorizontalSpace = true; gd.horizontalAlignment = SWT.FILL; fieldsComposite.setLayoutData(gd); final NonEmptyStringValidator idValidator = new NonEmptyStringValidator( "Inventory ID cannot be empty"); // inventoryID final BgcBaseText inventoryIdText = (BgcBaseText) createBoundWidgetWithLabel( fieldsComposite, BgcBaseText.class, SWT.NONE, "Inventory ID", new String[0], singleSpecimen, SpecimenPeer.INVENTORY_ID.getName(), idValidator, INVENTORY_ID_BINDING); inventoryIdText.addKeyListener(textFieldKeyListener); inventoryIdText.addFocusListener(new FocusAdapter() { @Override public void focusLost(FocusEvent e) { if (inventoryIdModified && idValidator.validate(inventoryIdText.getText()) == Status.OK_STATUS) { BusyIndicator.showWhile(PlatformUI.getWorkbench() .getActiveWorkbenchWindow().getShell().getDisplay(), new Runnable() { @Override public void run() { checkInventoryId(inventoryIdText); } }); } inventoryIdModified = false; } }); inventoryIdText.addModifyListener(new ModifyListener() { @Override public void modifyText(ModifyEvent e) { inventoryIdModified = true; newSinglePositionText.setText(""); canSaveSingleSpecimen.setValue(false); } }); // position field newSinglePositionLabel = widgetCreator.createLabel(fieldsComposite, "Position"); newSinglePositionValidator = new StringLengthValidator(4, "Enter a position"); newSinglePositionText = (BgcBaseText) widgetCreator.createBoundWidget( fieldsComposite, BgcBaseText.class, SWT.NONE, newSinglePositionLabel, new String[0], new WritableValue("", String.class), newSinglePositionValidator, NEW_SINGLE_POSITION_BINDING); newSinglePositionText.addFocusListener(new FocusAdapter() { @Override public void focusLost(FocusEvent e) { if (positionTextModified && newSinglePositionValidator .validate(newSinglePositionText.getText()) == Status.OK_STATUS) { BusyIndicator.showWhile(PlatformUI.getWorkbench() .getActiveWorkbenchWindow().getShell().getDisplay(), new Runnable() { @Override public void run() { initContainersFromPosition( newSinglePositionText, false, null); checkPositionAndSpecimen(inventoryIdText, newSinglePositionText); } }); } positionTextModified = false; } }); newSinglePositionText.addModifyListener(new ModifyListener() { @Override public void modifyText(ModifyEvent e) { positionTextModified = true; displaySinglePositions(false); canSaveSingleSpecimen.setValue(false); } }); newSinglePositionText .addKeyListener(EnterKeyToNextFieldListener.INSTANCE); // widget to select the source and the type singleTypesWidget = new AliquotedSpecimenSelectionWidget( fieldsComposite, null, widgetCreator, false); singleTypesWidget.addBindings(); } private void checkInventoryId(BgcBaseText inventoryIdText) { log.debug("checkInventoryId: " + inventoryIdText.getText()); boolean ok = true; try { SpecimenWrapper specimen = SpecimenWrapper.getSpecimen( SessionManager.getAppService(), inventoryIdText.getText()); if (specimen != null) { BgcPlugin .openAsyncError( "InventoryId error", NLS.bind( "InventoryId {0} already exists.", inventoryIdText.getText())); ok = false; } } catch (Exception e) { BgcPlugin .openAsyncError( "Error checking inventoryId", e); ok = false; } singleTypesWidget.setEnabled(ok); newSinglePositionText.setEnabled(ok); canSaveSingleSpecimen.setValue(ok); if (!ok) focusControl(inventoryIdText); } @Override protected void setBindings(boolean isSingleMode) { super.setBindings(isSingleMode); widgetCreator.setBinding(INVENTORY_ID_BINDING, isSingleMode); singleTypesWidget.removeBindings(); if (isSingleMode) { singleTypesWidget.addBindings(); singleTypesWidget.deselectAll(); } } @Override protected void updateAvailableSpecimenTypes() { log.debug("updateAvailableSpecimenTypes"); setTypeCombos(); } /** * Get types only defined in the patient's study. Then set these types to * the types combos * * @param typesRowss used only in multiple to indicate the count of each row * after scan has been done. * @throws ApplicationException */ private void setTypeCombos() { log.debug("setTypeCombos"); List<SpecimenTypeWrapper> studiesAliquotedTypes = null; List<SpecimenTypeWrapper> authorizedTypesInContainers = null; if (isSingleMode()) { log.debug("setTypeCombos: single mode"); if ((parentContainers != null) && (parentContainers.size() >= 1)) { authorizedTypesInContainers = parentContainers.get(0).getContainerType() .getSpecimenTypeCollection(); } } else { log.debug("setTypeCombos: multiple mode"); /* * If the current center is a site, and if this site defines * containers of 8*12 size, then get the specimen types these * containers can contain */ if (SessionManager.getUser().getCurrentWorkingSite() != null) { List<SpecimenTypeWrapper> res = null; try { res = SpecimenTypeWrapper.getSpecimenTypeForPallet96( SessionManager.getAppService(), SessionManager .getUser() .getCurrentWorkingSite()); } catch (ApplicationException e) { BgcPlugin.openAsyncError("Error", "Failed to retrieve specimen types."); } if (res.size() != 0) authorizedTypesInContainers = res; } } studiesAliquotedTypes = linkFormPatientManagement .getStudyAliquotedTypes(authorizedTypesInContainers); List<SpecimenWrapper> availableSourceSpecimens = linkFormPatientManagement .getParentSpecimenForPEventAndCEvent(); if (authorizedTypesInContainers != null) { // availableSourceSpecimen should be parents of the authorized Types // ! List<SpecimenWrapper> filteredSpecs = new ArrayList<SpecimenWrapper>(); for (SpecimenWrapper spec : availableSourceSpecimens) if (!Collections.disjoint(authorizedTypesInContainers, spec .getSpecimenType().getChildSpecimenTypeCollection(false))) filteredSpecs.add(spec); availableSourceSpecimens = filteredSpecs; } // for single singleTypesWidget.resetValues(true, false); singleTypesWidget.setSourceSpecimens(availableSourceSpecimens); singleTypesWidget.setResultTypes(studiesAliquotedTypes); if (!isSingleMode()) for (int row = 0; row < specimenTypesWidgets.size(); row++) { AliquotedSpecimenSelectionWidget widget = specimenTypesWidgets .get(row); // if rescan, want to keep previous selection SpecimenHierarchyInfo previousSelection = widget.getSelection(); widget.setSourceSpecimens(availableSourceSpecimens); widget.setResultTypes(studiesAliquotedTypes); widget.setSelection(previousSelection); } } @Override protected boolean fieldsValid() { return (mode.isSingleMode() ? true : isPlateValid()) && linkFormPatientManagement.fieldsValid(); } @Override protected void doBeforeSave() throws Exception { log.debug("doBeforeSave"); // can't access the combos in another thread, so do it now if (mode.isSingleMode()) { SpecimenHierarchyInfo selection = singleTypesWidget.getSelection(); singleSpecimen.setParentSpecimen(selection.getParentSpecimen()); singleSpecimen .setSpecimenType(selection.getAliquotedSpecimenType()); singleSpecimen.setCollectionEvent(linkFormPatientManagement .getSelectedCollectionEvent()); } } @Override protected void saveForm() throws Exception { log.debug("saveForm"); if (mode.isSingleMode()) saveSingleSpecimen(); else saveMultipleSpecimens(); setFinished(false); SessionManager.log("save", null, "SpecimenLink"); } private void saveMultipleSpecimens() throws Exception { log.debug("saveForm"); @SuppressWarnings("unchecked") Map<RowColPos, PalletCell> cells = (Map<RowColPos, PalletCell>) palletWidget .getCells(); List<AliquotedSpecimenInfo> asiList = new ArrayList<AliquotedSpecimenInfo>(); for (PalletCell cell : cells.values()) { if (PalletCell.hasValue(cell) && cell.getStatus() == UICellStatus.TYPE) { SpecimenWrapper sourceSpecimen = cell.getSourceSpecimen(); SpecimenWrapper aliquotedSpecimen = cell.getSpecimen(); AliquotedSpecimenInfo asi = new AliquotedSpecimenInfo(); asi.activityStatus = ActivityStatus.ACTIVE; asi.typeId = aliquotedSpecimen.getSpecimenType().getId(); asi.inventoryId = cell.getValue(); asi.parentSpecimenId = sourceSpecimen.getId(); asiList.add(asi); } } List<AliquotedSpecimenResInfo> resList = SessionManager.getAppService() .doAction( new SpecimenLinkSaveAction(SessionManager.getUser() .getCurrentWorkingCenter().getId(), linkFormPatientManagement.getCurrentPatient().getStudy() .getId(), asiList)).getList(); printSaveMultipleLogMessage(resList); } protected void printSaveMultipleLogMessage( List<AliquotedSpecimenResInfo> resList) { log.debug("printSaveMultipleLogMessage"); StringBuffer sb = new StringBuffer( "ALIQUOTED SPECIMENS:\n"); for (AliquotedSpecimenResInfo resInfo : resList) { sb.append(MessageFormat .format( "LINKED: ''{0}'' with type ''{1}'' to source: {2} ({3}) - Patient: {4} - Visit: {5} - Center: {6}\n", resInfo.inventoryId, resInfo.typeName, resInfo.parentTypeName, resInfo.parentInventoryId, resInfo.patientPNumber, resInfo.visitNumber, resInfo.currentCenterName)); } // Want only one common 'log entry' so use a stringbuffer to print // everything together appendLog(sb.toString()); // LINKING\: {0} specimens linked to patient {1} on center {2} appendLog(MessageFormat.format( "LINKING: {0} specimens linked to patient {1} on center {2}", resList .size(), linkFormPatientManagement.getCurrentPatient() .getPnumber(), SessionManager.getUser() .getCurrentWorkingCenter().getNameShort())); } private void saveSingleSpecimen() throws Exception { log.debug("saveSingleSpecimen"); AliquotedSpecimenInfo asi = new AliquotedSpecimenInfo(); asi.activityStatus = ActivityStatus.ACTIVE; asi.typeId = singleSpecimen.getSpecimenType().getId(); if (singleSpecimen.getParentContainer() != null) { asi.containerId = singleSpecimen.getParentContainer().getId(); asi.position = singleSpecimen.getPosition(); } asi.inventoryId = singleSpecimen.getInventoryId(); asi.parentSpecimenId = singleSpecimen.getParentSpecimen().getId(); List<AliquotedSpecimenResInfo> resList = SessionManager.getAppService() .doAction( new SpecimenLinkSaveAction(SessionManager.getUser() .getCurrentWorkingCenter().getId(), linkFormPatientManagement.getCurrentPatient().getStudy() .getId(), Arrays.asList(asi))).getList(); printSaveSingleLogMessage(resList); } protected void printSaveSingleLogMessage( List<AliquotedSpecimenResInfo> resList) { log.debug("printSaveSingleLogMessage"); if (resList.size() == 1) { AliquotedSpecimenResInfo resInfo = resList.get(0); String posStr = resInfo.position; if (posStr == null) { posStr = "none"; } appendLog(MessageFormat .format( "LINKED: ''{0}'' with type ''{1}'' to source: {2} ({3}) - Patient: {4} - Visit: {5} - Center: {6} - Position: {7}\n", resInfo.inventoryId, resInfo.typeName, resInfo.parentInventoryId, resInfo.parentTypeName, resInfo.patientPNumber, resInfo.visitNumber, resInfo.currentCenterName, posStr)); } else { throw new RuntimeException("Result size incorrect"); } } @Override public void setValues() throws Exception { log.debug("setValues"); super.setValues(); if (isSingleMode()) singleTypesWidget.deselectAll(); showOnlyPallet(true); form.layout(true, true); } @Override public void reset(boolean resetAll) { log.debug("reset: " + resetAll); linkFormPatientManagement.reset(resetAll); if (resetAll) { palletWidget.setCells(null); for (AliquotedSpecimenSelectionWidget stw : specimenTypesWidgets) { stw.resetValues(true, true); } } super.reset(resetAll); } @Override public boolean onClose() { log.debug("onClose"); linkFormPatientManagement.onClose(); return super.onClose(); } @Override /** * Multiple linking: do this before multiple scan is made */ protected void beforeScanThreadStart() { log.debug("beforeScanThreadStart"); super.beforeScanThreadStart(); setTypeCombos(); beforeScans(true); } @Override /** * Multiple linking: do this before scan of one tube is really made */ protected void beforeScanTubeAlone() { log.debug("beforeScanTubeAlone"); super.beforeScanTubeAlone(); beforeScans(false); } /** * Multiple linking: do this before any scan is really launched */ private void beforeScans(boolean resetTypeRows) { log.debug("beforeScans: " + resetTypeRows); preSelections = new ArrayList<SpecimenHierarchyInfo>(); for (AliquotedSpecimenSelectionWidget stw : specimenTypesWidgets) { preSelections.add(stw.getSelection()); } if (resetTypeRows) typesRows.clear(); } @Override /** * Multiple linking: return fake cells for testing */ protected Map<RowColPos, PalletCell> getFakeScanCells() throws Exception { if (isFakeScanRandom) { return PalletCell.getRandomScanLink(); } try { return PalletCell.getRandomScanLinkWithSpecimensAlreadyLinked( SessionManager.getAppService(), SessionManager.getUser() .getCurrentWorkingCenter().getId()); } catch (Exception ex) { BgcPlugin.openAsyncError("Fake Scan problem", ex); } return null; } @Override protected Action<ProcessResult> getCellProcessAction(Integer centerId, CellInfo cell, Locale locale) { log.debug("getCellProcessAction"); return new SpecimenLinkProcessAction(centerId, linkFormPatientManagement .getCurrentPatient().getStudy().getId(), cell, locale); } @Override protected Action<ProcessResult> getPalletProcessAction( Integer centerId, Map<RowColPos, CellInfo> cells, boolean isRescanMode, Locale locale) { log.debug("getPalletProcessAction"); return new SpecimenLinkProcessAction(centerId, linkFormPatientManagement .getCurrentPatient().getStudy().getId(), cells, isRescanMode, locale); } @Override /** * Multiple linking: do this after multiple scan has been launched * @param rowToProcess is null if scanning everything, is the current row if is scanning a single cell */ protected void afterScanAndProcess(final Integer rowToProcess) { Display.getDefault().asyncExec(new Runnable() { @Override public void run() { log.debug("afterScanAndProcess: asyncExec"); // enabled the hierarchy combos typesSelectionPerRowComposite .setEnabled(currentScanState != UICellStatus.ERROR); // set the combos lists if (typesRows.size() > 0) Display.getDefault().asyncExec(new Runnable() { @Override public void run() { if (rowToProcess == null) { if (typesRows != null) for (int row = 0; row < specimenTypesWidgets .size(); row++) setCountOnSpecimenWidget(typesRows, row); } else setCountOnSpecimenWidget(typesRows, rowToProcess); } }); // focus first available list for (AliquotedSpecimenSelectionWidget typeWidget : specimenTypesWidgets) { if (typeWidget.canFocus()) { typeWidget.setFocus(); break; } } // Show result in grid palletWidget.setCells(getCells()); setRescanMode(); } }); } /** * Multiple linking: Post process of only one cell after server call has * been made */ @Override protected void processCellResult(final RowColPos rcp, PalletCell cell) { log.debug("processCellResult"); Integer typesRowsCount = typesRows.get(rcp.getRow()); if (typesRowsCount == null) { typesRowsCount = 0; specimenTypesWidgets.get(rcp.getRow()).resetValues(!isRescanMode(), true, true); } SpecimenHierarchyInfo selection = preSelections.get(cell.getRow()); if (selection != null) setHierarchyToCell(cell, selection); if (PalletCell.hasValue(cell)) { typesRowsCount++; typesRows.put(rcp.getRow(), typesRowsCount); } } /** * Multiple linking: apply a count on a specific row of 'hierarchy' widgets. * * @param typesRows contains a row:count map * @param row row of the widget we want to update. */ private void setCountOnSpecimenWidget(Map<Integer, Integer> typesRows, int row) { log.debug("setCountOnSpecimenWidget"); AliquotedSpecimenSelectionWidget widget = specimenTypesWidgets.get(row); Integer number = typesRows.get(row); if (number == null) { number = 0; widget.deselectAll(); } widget.setNumber(number); } /** * Multiple linking: apply a source and type to a specific cell. */ private void setHierarchyToCell(PalletCell cell, SpecimenHierarchyInfo selection) { log.debug("setHierarchyToCell"); cell.setSourceSpecimen(selection.getParentSpecimen()); cell.setSpecimenType(selection.getAliquotedSpecimenType()); if (cell.getStatus() != UICellStatus.ERROR) cell.setStatus(UICellStatus.TYPE); } @Override protected Mode initialisationMode() { log.debug("initialisationMode: " + mode); return mode; } @Override protected void enableFields(boolean enable) { log.debug("enableFields: " + enable); super.enableFields(enable); multipleOptionsFields.setEnabled(enable); } }