/*
* Copyright (C) 2013 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.place.shared.PlaceController;
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.Record;
import com.smartgwt.client.types.Overflow;
import com.smartgwt.client.types.ReadOnlyDisplayAppearance;
import com.smartgwt.client.types.SelectionStyle;
import com.smartgwt.client.types.TitleOrientation;
import com.smartgwt.client.util.BooleanCallback;
import com.smartgwt.client.widgets.Canvas;
import com.smartgwt.client.widgets.Label;
import com.smartgwt.client.widgets.form.DynamicForm;
import com.smartgwt.client.widgets.form.ValuesManager;
import com.smartgwt.client.widgets.form.fields.TextItem;
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.DataArrivedEvent;
import com.smartgwt.client.widgets.grid.events.DataArrivedHandler;
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.toolbar.ToolStrip;
import cz.cas.lib.proarc.webapp.client.ClientMessages;
import cz.cas.lib.proarc.webapp.client.ClientUtils;
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.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.DeleteAction;
import cz.cas.lib.proarc.webapp.client.action.DeleteAction.RecordDeletable;
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.SaveAction;
import cz.cas.lib.proarc.webapp.client.action.SaveAction.Savable;
import cz.cas.lib.proarc.webapp.client.action.SaveAction.SaveValidation;
import cz.cas.lib.proarc.webapp.client.action.Selectable;
import cz.cas.lib.proarc.webapp.client.ds.DeviceDataSource;
import cz.cas.lib.proarc.webapp.client.ds.LanguagesDataSource;
import cz.cas.lib.proarc.webapp.client.ds.RestConfig;
import cz.cas.lib.proarc.webapp.client.widget.StatusView;
import cz.cas.lib.proarc.webapp.client.widget.form.FormGenerator;
import cz.cas.lib.proarc.webapp.shared.form.Field;
import cz.cas.lib.proarc.webapp.shared.form.FieldBuilder;
import cz.cas.lib.proarc.webapp.shared.form.Form;
/**
* The device manager UI to search, add, remove or edit devices.
*
* @author Jan Pokorsky
*/
public final class DeviceManager {
private DeviceManagerWidget ui;
private final ClientMessages i18n;
public DeviceManager(ClientMessages i18n, PlaceController places) {
this.i18n = i18n;
}
public void init() {
ui.refresh();
}
public Canvas getUI() {
if (ui == null) {
ui = new DeviceManagerWidget(i18n);
}
return ui.asWidget();
}
private static final class DeviceManagerWidget implements Refreshable, Selectable<Record> {
private final ClientMessages i18n;
private final VLayout widget;
private final ListGrid deviceList;
private final DynamicForm form;
private final ActionSource actionSource;
private Record lastSelection;
/** The flag cancels onDataArrived as onDataArrived is invoked before addData callback. */
private boolean addingDevice = false;
/** The flag to do not fetch data after update. */
private boolean updatingDevice = false;
private final DynamicForm descriptionForm;
ValuesManager valuesManager;
public DeviceManagerWidget(ClientMessages i18n) {
this.i18n = i18n;
actionSource = new ActionSource(this);
widget = new VLayout();
Label lblHeader = new Label();
String title = ClientUtils.format("<b>%s</b>", i18n.DeviceManager_Title());
lblHeader.setContents(title);
lblHeader.setAutoHeight();
lblHeader.setPadding(4);
lblHeader.setStyleName(Editor.CSS_PANEL_DESCRIPTION_TITLE);
ToolStrip toolbar = createToolbar(i18n);
deviceList = createDeviceList(i18n);
form = createForm();
descriptionForm = createDescriptionForm();
valuesManager = new ValuesManager();
valuesManager.addMember(form);
valuesManager.addMember(descriptionForm);
valuesManager.setDataSource(DeviceDataSource.getInstance());
VLayout forms = new VLayout();
forms.setOverflow(Overflow.AUTO);
forms.setMembers(form, descriptionForm);
HLayout hLayout = new HLayout();
deviceList.setWidth100();
forms.setWidth100();
hLayout.setMembers(deviceList, forms);
widget.addMember(lblHeader);
widget.addMember(toolbar);
widget.addMember(hLayout);
}
public VLayout asWidget() {
return widget;
}
@Override
public void refresh() {
valuesManager.clearValues();
deviceList.invalidateCache();
deviceList.fetchData();
}
@Override
public Record[] getSelection() {
return deviceList.getSelectedRecords();
}
private ListGrid createDeviceList(ClientMessages i18n) {
final ListGrid lg = new ListGrid();
lg.setSelectionType(SelectionStyle.SINGLE);
ListGridField fieldLabel = new ListGridField(DeviceDataSource.FIELD_LABEL);
lg.setDataSource(DeviceDataSource.getInstance(), fieldLabel);
lg.setSortField(DeviceDataSource.FIELD_LABEL);
lg.addSelectionUpdatedHandler(new SelectionUpdatedHandler() {
@Override
public void onSelectionUpdated(SelectionUpdatedEvent event) {
onDeviceSelect();
}
});
lg.addDataArrivedHandler(new DataArrivedHandler() {
@Override
public void onDataArrived(DataArrivedEvent event) {
if (addingDevice) {
return ;
}
int index = 0;
boolean notEmpty = !lg.getDataAsRecordList().isEmpty();
if (lastSelection != null && notEmpty) {
String lastId = lastSelection.getAttribute(DeviceDataSource.FIELD_ID);
index = lg.getDataAsRecordList().findIndex(DeviceDataSource.FIELD_ID, lastId);
index = Math.max(0, index);
}
if (notEmpty) {
lg.scrollToRow(index);
lg.selectSingleRecord(index);
} else {
actionSource.fireEvent();
}
}
});
return lg;
}
private void onDeviceSelect() {
if (updatingDevice) {
return ;
}
ListGridRecord record = deviceList.getSelectedRecord();
lastSelection = record;
if (record != null) {
fetchDescription(record.getAttribute(DeviceDataSource.FIELD_ID));
} else {
valuesManager.clearValues();
descriptionForm.clearValues();
}
actionSource.fireEvent();
}
private void fetchDescription(String id) {
Criteria criteria = new Criteria(DeviceDataSource.FIELD_ID, id);
valuesManager.fetchData(criteria, new DSCallback() {
@Override
public void execute(DSResponse response, Object dataObj, DSRequest dsRequest) {
if (RestConfig.isStatusOk(response)) {
Record[] data = response.getData();
if (data != null && data.length == 1) {
setDescription(null);
}
}
}
});
}
private void setDescription(Record deviceRecord) {
clearErrors(true);
if (deviceRecord != null) {
valuesManager.editRecord(deviceRecord);
}
}
private DynamicForm createForm() {
DynamicForm df = new DynamicForm();
df.setMargin(4);
df.setNumCols(1);
df.setTitleOrientation(TitleOrientation.TOP);
df.setBrowserSpellCheck(false);
df.setDataSource(DeviceDataSource.getInstance());
TextItem fieldId = new TextItem(DeviceDataSource.FIELD_ID);
fieldId.setWidth(280);
fieldId.setCanEdit(false);
fieldId.setReadOnlyDisplay(ReadOnlyDisplayAppearance.STATIC);
TextItem fieldLabel = new TextItem(DeviceDataSource.FIELD_LABEL);
fieldLabel.setRequired(true);
fieldLabel.setWidth("*");
df.setItems(fieldId, fieldLabel);
return df;
}
private DynamicForm createDescriptionForm() {
Form f = new Form();
f.getFields().add(
new FieldBuilder("description").setMaxOccurrences(1)
.addField(new FieldBuilder("ImageCaptureMetadata").setMaxOccurrences(1)
.addField(new FieldBuilder("GeneralCaptureInformation").setTitle("General Capture Information - M").setMaxOccurrences(1)
.setHint("Základní údaje o skenování.")
// dateTimeCreated, typeOfDateType - should be taken from scan?
// .addField(new FieldBuilder("dateTimeCreated").setMaxOccurrences(1).createField())
// imageProducer, stringType(value, use)
.addField(new FieldBuilder("imageProducer").setMaxOccurrences(1)
.addField(new FieldBuilder("value").setTitle("Image Producer - M").setMaxOccurrences(1).setRequired(true)
.setType(Field.TEXT).setHint("Entita provádějící skenování. Např. The National Library of the Czech Republic, osoba apod.").createField())
.createField()) // imageProducer
// captureDevice, typeOfCaptureDeviceType extends captureDeviceType
.addField(new FieldBuilder("captureDevice").setMaxOccurrences(1)
// @use, value(enum)
.addField(new FieldBuilder("value").setTitle("Capture Device - MA").setMaxOccurrences(1).setType(Field.SELECT)
.setHint("Typ skenovacího zařízení.")
.addMapValue("transmission scanner", "transmission scanner")
.addMapValue("reflection print scanner", "reflection print scanner")
.addMapValue("digital still camera", "digital still camera")
.addMapValue("still from video", "still from video")
.createField()) // value
.createField()) // captureDevice
.createField()) // GeneralCaptureInformation
// ScannerCapture
.addField(new FieldBuilder("ScannerCapture").setTitle("Scanner Capture - M").setMaxOccurrences(1)
.setHint("Údaje o skeneru.")
// scannerManufacturer, stringType(value, use)
.addField(new FieldBuilder("scannerManufacturer").setMaxOccurrences(1)
.addField(new FieldBuilder("value").setTitle("Scanner Manufacturer - M").setMaxOccurrences(1).setRequired(true)
.setType(Field.COMBO).setHint("Výrobce skeneru, např. 4DigitalBooks, Treventus, Zeutschel.")
.addMapValue("Zeutschel", "Zeutschel")
.addMapValue("Treventus", "Treventus")
.addMapValue("4DigitalBooks", "4DigitalBooks")
.createField()) // value
.createField()) // imageProducer
// ScannerModel
.addField(new FieldBuilder("ScannerModel").setTitle("Scanner Model - M").setMaxOccurrences(1)
.setHint("Údaje o konkrétním typu skeneru.")
// scannerModelName, stringType
.addField(new FieldBuilder("scannerModelName").setMaxOccurrences(1)
.addField(new FieldBuilder("value").setTitle("Scanner Model Name - M").setMaxOccurrences(1).setRequired(true)
.setType(Field.TEXT).setHint("Jméno modelové řady skeneru, např. DL.")
.createField()) // value
.createField()) // scannerModelName
// scannerModelNumber, stringType
.addField(new FieldBuilder("scannerModelNumber").setMaxOccurrences(1)
.addField(new FieldBuilder("value").setTitle("Scanner Model Number - M").setMaxOccurrences(1).setRequired(true)
.setType(Field.TEXT).setHint("Číslo/označení modelu, např. 3000.")
.createField()) // value
.createField()) // scannerModelNumber
// scannerModelNumber, stringType
.addField(new FieldBuilder("scannerModelSerialNo").setMaxOccurrences(1)
.addField(new FieldBuilder("value").setTitle("Scanner Model Serial No - M").setMaxOccurrences(1).setRequired(true)
.setType(Field.TEXT).setHint("Výrobní číslo skeneru, např. E4R0003649.")
.createField()) // value
.createField()) // scannerModelSerialNo
.createField()) // ScannerModel
// MaximumOpticalResolution
.addField(new FieldBuilder("MaximumOpticalResolution").setTitle("Maximum Optical Resolution - M").setMaxOccurrences(1)
.setHint("Údaje o maximálním optickém rozlišení skeneru.")
// xOpticalResolution, positiveIntegerType extends positiveInteger extends xsd:integer
.addField(new FieldBuilder("xOpticalResolution").setMaxOccurrences(1)
// value, @use
.addField(new FieldBuilder("value").setTitle("X Optical Resolution - M").setMaxOccurrences(1).setRequired(true)
.setType(Field.TEXT).setHint("Optické rozlišení na ose x, např. 300.")
.createField()) // value
.createField()) // xOpticalResolution
// yOpticalResolution, positiveIntegerType extends positiveInteger extends xsd:integer
.addField(new FieldBuilder("yOpticalResolution").setMaxOccurrences(1)
// value, @use
.addField(new FieldBuilder("value").setTitle("Y Optical Resolution - M").setMaxOccurrences(1).setRequired(true)
.setType(Field.TEXT).setHint("Optické rozlišení na ose y, např. 300.")
.createField()) // value
.createField()) // yOpticalResolution
// opticalResolutionUnit, typeOfOpticalResolutionUnitType extends opticalResolutionUnitType base opticalResolutionUnitType
.addField(new FieldBuilder("opticalResolutionUnit").setMaxOccurrences(1)
// value, @use
.addField(new FieldBuilder("value").setTitle("Optical Resolution Unit - M").setMaxOccurrences(1).setRequired(true)
.setType(Field.SELECT).setHint("Jednotka optického rozlišení, např. inch (in.).").setWidth("150")
.addMapValue("cm", "cm")
.addMapValue("in.", "in.")
.addMapValue("no absolute unit", "no absolute unit")
.createField()) // value
.createField()) // opticalResolutionUnit
.createField()) // MaximumOpticalResolution
// scannerSensor, typeOfScannerSensorType extends scannerSensorType(enum)
.addField(new FieldBuilder("scannerSensor").setMaxOccurrences(1)
// value, @use
.addField(new FieldBuilder("value").setTitle("Scanner Sensor - M").setMaxOccurrences(1).setRequired(true)
.setType(Field.SELECT).setHint("Popis typu snímacího senzoru skenovacího zařízení.")
.addMapValue("undefined", "undefined")
.addMapValue("MonochromeLinear", "MonochromeLinear")
.addMapValue("ColorTriLinear", "ColorTriLinear")
.addMapValue("ColorSequentialLinear", "ColorSequentialLinear")
.addMapValue("MonochromeArea", "MonochromeArea")
.addMapValue("OneChipColourArea", "OneChipColourArea")
.addMapValue("TwoChipColorArea", "TwoChipColorArea")
.addMapValue("ThreeChipColorArea", "ThreeChipColorArea")
.addMapValue("ColorSequentialArea", "ColorSequentialArea")
.createField()) // value
.createField()) // scannerSensor
.addField(new FieldBuilder("ScanningSystemSoftware").setTitle("Scanning System Software - M").setMaxOccurrences(1)
.setHint("Údaje o softwaru skenovacího zařízení.")
// scanningSoftwareName, stringType
.addField(new FieldBuilder("scanningSoftwareName").setMaxOccurrences(1)
// value, @use
.addField(new FieldBuilder("value").setTitle("Scanning Software Name - M").setMaxOccurrences(1).setRequired(true)
.setType(Field.TEXT).setHint("Název softwaru.")
.createField()) // value
.createField()) // scanningSoftwareName
// scanningSoftwareVersionNo, stringType
.addField(new FieldBuilder("scanningSoftwareVersionNo").setMaxOccurrences(1)
// value, @use
.addField(new FieldBuilder("value").setTitle("Scanning Software Version No - M").setMaxOccurrences(1)
.setType(Field.TEXT).setHint("Číslo verze softwaru, např. 3.7.").setRequired(true)
.createField()) // value
.createField()) // scanningSoftwareVersionNo
.createField()) // ScanningSystemSoftware
.createField()) // ScannerCapture
.addField(new FieldBuilder("DigitalCameraCapture").setTitle("Digital Camera Capture - MA").setMaxOccurrences(1)
.setHint("Údaje o snímacím zařízení (fotoaparát).<p>Povinné, pokud je používán fotoaparát a není používán skener.")
// digitalCameraManufacturer, stringType
.addField(new FieldBuilder("digitalCameraManufacturer").setMaxOccurrences(1)
// value, @use
.addField(new FieldBuilder("value").setTitle("Digital Camera Manufacturer - M").setMaxOccurrences(1)
.setType(Field.TEXT).setHint("Výrobce fotoaparátu, např. Canon.")
.createField()) // value
.createField()) // digitalCameraManufacturer
.addField(new FieldBuilder("DigitalCameraModel").setTitle("DigitalCameraModel").setMaxOccurrences(1)
.setHint("Popis modelu fotoaparátu")
// digitalCameraModelName, stringType
.addField(new FieldBuilder("digitalCameraModelName").setMaxOccurrences(1)
// value, @use
.addField(new FieldBuilder("value").setTitle("Digital Camera Model Name - M").setMaxOccurrences(1)
.setType(Field.TEXT).setHint("Název modelové řady, např. EOS.")
.createField()) // value
.createField()) // digitalCameraModelName
// digitalCameraModelNumber, stringType
.addField(new FieldBuilder("digitalCameraModelNumber").setMaxOccurrences(1)
// value, @use
.addField(new FieldBuilder("value").setTitle("Digital Camera Model Number - M").setMaxOccurrences(1)
.setType(Field.TEXT).setHint("Označení modelu fotoaparátu, např. 1000D.")
.createField()) // value
.createField()) // digitalCameraModelNumber
// digitalCameraModelSerialNo, stringType
.addField(new FieldBuilder("digitalCameraModelSerialNo").setMaxOccurrences(1)
// value, @use
.addField(new FieldBuilder("value").setTitle("Digital Camera Model Serial No - M").setMaxOccurrences(1)
.setType(Field.TEXT).setHint("výrobní číslo přístroje, např. E12345.")
.createField()) // value
.createField()) // digitalCameraModelNumber
.createField()) // DigitalCameraModel
// cameraSensor, typeOfCameraSensorType extends cameraSensorType(enum)
.addField(new FieldBuilder("cameraSensor").setMaxOccurrences(1)
// value, @use
.addField(new FieldBuilder("value").setTitle("Camera Sensor - M").setMaxOccurrences(1)
.setType(Field.SELECT).setHint("Typ senzoru fotoaparátu.")
.addMapValue("undefined", "undefined")
.addMapValue("MonochromeArea", "MonochromeArea")
.addMapValue("OneChipColorArea", "OneChipColorArea")
.addMapValue("TwoChipColorArea", "TwoChipColorArea")
.addMapValue("ThreeChipColorArea", "ThreeChipColorArea")
.addMapValue("MonochromeLinear", "MonochromeLinear")
.addMapValue("ColorTriLinear", "ColorTriLinear")
.addMapValue("ColorSequentialLinear", "ColorSequentialLinear")
.addMapValue("", "")
.createField()) // value
.createField()) // cameraSensor
.createField()) // DigitalCameraCapture
.createField()) // ImageCaptureMetadata
.createField()); // mix
return new FormGenerator(f, LanguagesDataSource.activeLocale()).generateForm();
}
private ToolStrip createToolbar(ClientMessages i18n) {
RefreshAction refreshAction = new RefreshAction(i18n);
AbstractAction addAction = new AbstractAction(i18n.DeviceManager_Add_Title(),
"[SKIN]/actions/add.png", i18n.DeviceManager_Add_Hint()) {
@Override
public void performAction(ActionEvent event) {
addDevice();
}
};
SaveAction saveAction = new SaveAction(i18n) {
@Override
public void performAction(ActionEvent event) {
save();
}
@Override
public boolean accept(ActionEvent event) {
return getSelection().length == 1;
}
};
DeleteAction deleteAction = new DeleteAction(
new RecordDeletable(DeviceDataSource.getInstance(), i18n),
i18n);
ToolStrip t = Actions.createToolStrip();
t.addMember(Actions.asIconButton(refreshAction, this));
t.addMember(Actions.asIconButton(addAction, this));
t.addMember(Actions.asIconButton(deleteAction, actionSource));
t.addMember(Actions.asIconButton(saveAction, actionSource));
return t;
}
private void save() {
ListGridRecord r = deviceList.getSelectedRecord();
if (r != null) {
if (!form.validate()) {
return ;
}
SaveAction.saveTask(new Savable() {
@Override
public void save(BooleanCallback result) {
saveImpl(result);
}
@Override
public void validate(BooleanCallback result) {
Boolean valid = descriptionForm.validate();
result.execute(valid);
}
}, ClientUtils.EMPTY_BOOLEAN_CALLBACK, true, SaveValidation.ASK, i18n);
}
}
private void saveImpl(final BooleanCallback callback) {
Record update = new Record(valuesManager.getValues());
update = ClientUtils.normalizeData(update);
updatingDevice = true;
DeviceDataSource.getInstance().updateData(update, new DSCallback() {
@Override
public void execute(DSResponse response, Object rawData, DSRequest request) {
updatingDevice = false;
boolean status = RestConfig.isStatusOk(response);
if (status) {
StatusView.getInstance().show(i18n.SaveAction_Done_Msg());
Record[] data = response.getData();
if (data != null && data.length == 1) {
Record deviceRecord = data[0];
setDescription(deviceRecord);
}
}
callback.execute(status);
}
});
}
private void addDevice() {
addingDevice = true;
deviceList.addData(new Record(), new DSCallback() {
@Override
public void execute(DSResponse response, Object rawData, DSRequest request) {
addingDevice = false;
if (RestConfig.isStatusOk(response)) {
StatusView.getInstance().show(i18n.SaveAction_Done_Msg());
Record r = response.getData()[0];
deviceList.selectSingleRecord(r);
}
}
});
}
private void clearErrors(boolean show) {
form.clearErrors(show);
descriptionForm.clearErrors(show);
}
}
}