/* * Copyright (C) 2011 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.core.client.GWT; import com.google.gwt.safehtml.shared.SafeHtmlUtils; import com.smartgwt.client.i18n.SmartGwtMessages; import com.smartgwt.client.types.Alignment; import com.smartgwt.client.util.BooleanCallback; import com.smartgwt.client.widgets.Canvas; import com.smartgwt.client.widgets.IButton; import com.smartgwt.client.widgets.Window; import com.smartgwt.client.widgets.form.DynamicForm; import com.smartgwt.client.widgets.form.events.SubmitValuesEvent; import com.smartgwt.client.widgets.form.events.SubmitValuesHandler; import com.smartgwt.client.widgets.form.fields.CheckboxItem; import com.smartgwt.client.widgets.form.fields.ComboBoxItem; import com.smartgwt.client.widgets.form.fields.FormItem; import com.smartgwt.client.widgets.form.fields.IntegerItem; import com.smartgwt.client.widgets.form.fields.SelectItem; import com.smartgwt.client.widgets.form.fields.StaticTextItem; import com.smartgwt.client.widgets.form.fields.TextItem; import com.smartgwt.client.widgets.form.fields.events.ChangedEvent; import com.smartgwt.client.widgets.form.fields.events.ChangedHandler; import com.smartgwt.client.widgets.form.validator.IntegerRangeValidator; import com.smartgwt.client.widgets.form.validator.IsIntegerValidator; import com.smartgwt.client.widgets.form.validator.RegExpValidator; import com.smartgwt.client.widgets.form.validator.RequiredIfFunction; import com.smartgwt.client.widgets.form.validator.RequiredIfValidator; import com.smartgwt.client.widgets.layout.HStack; import com.smartgwt.client.widgets.layout.VLayout; import cz.cas.lib.proarc.common.i18n.BundleValue; import cz.cas.lib.proarc.webapp.client.ClientMessages; import cz.cas.lib.proarc.webapp.client.ClientUtils; import cz.cas.lib.proarc.webapp.client.ds.ModsCustomDataSource; import cz.cas.lib.proarc.webapp.client.ds.ValueMapDataSource; import cz.cas.lib.proarc.webapp.shared.series.Series; import java.util.Iterator; import java.util.LinkedHashMap; /** * Editor allowing user input of new values to modify multiple digital objects. It can * be used as a standalone {@link #getFormPanel() panel} or as a {@link #showInWindow() window}. * * @author Jan Pokorsky */ public final class PageMetadataEditor { private static final String ALPHABET_LOWER_SERIES = "a - z, aa - az, ..."; private static final String ALPHABET_UPPER_SERIES = "A - Z, AA - AZ, ..."; private static final String ARABIC_SERIES = "1, 2, 3, 4, ..."; private static final String ROMAN_LOWER_SERIES = "i, ii, iii, iv, ..."; private static final String ROMAN_UPPER_SERIES = "I, II, III, IV, ..."; private CheckboxItem allowPageIndexes; private CheckboxItem allowPageNumbers; private CheckboxItem allowPageTypes; private IntegerItem indexStart; private TextItem numberStart; private IntegerItem numberIncrement; private SelectItem seriesType; private TextItem prefix; private TextItem suffix; private StaticTextItem numberExample; private SelectItem pageType; private ComboBoxItem applyTo; private IntegerRangeValidator integerStartValidator; private IntegerRangeValidator integerIncrementValidator; private RegExpValidator romanStartValidator; private RegExpValidator alphabetStartValidator; private RequiredIfValidator numberStartRequired; private Window window; private VLayout formPanel; private DynamicForm formApply; private DynamicForm formPageNumber; private DynamicForm formPageType; private DynamicForm formPageIndex; private BooleanCallback windowCallback; private final ClientMessages i18n; private final SubmitValuesHandler formSubmitHandler; private SubmitValuesHandler proxySubmitHandler; private int applyToMaxValue = 16; private IntegerRangeValidator applyToValidator; private String typeValueMapId; private CheckboxItem applyFromFirst; public PageMetadataEditor() { this.i18n = GWT.create(ClientMessages.class); formSubmitHandler = new SubmitValuesHandler() { @Override public void onSubmitValues(SubmitValuesEvent event) { if (proxySubmitHandler != null) { proxySubmitHandler.onSubmitValues(event); } } }; } public void setSubmitHandler(SubmitValuesHandler submitHandler) { this.proxySubmitHandler = submitHandler; } public Canvas getFormPanel() { if (formPanel != null) { return formPanel; } createPageIndexUi(); createPageNumberUi(); createPageTypeUi(); createApplyToUi(); formPanel = new VLayout(10); formPanel.setAutoHeight(); formPageIndex.setItems(allowPageIndexes, indexStart); formPageNumber.setItems(allowPageNumbers, prefix, numberStart, numberIncrement, suffix, seriesType, numberExample); formPageType.setItems(allowPageTypes, pageType); formPanel.setMembers(formPageType, formPageIndex, formPageNumber, formApply); initAll(); return formPanel; } public boolean validate() { if (formPanel != null) { boolean valid = true; boolean anyValue = false; if (getAllowPageIndexes()) { valid &= formPageIndex.validate(); anyValue = true; } if (getAllowPageNumbers()) { valid &= formPageNumber.validate(); anyValue = true; } if (getAllowPageTypes()) { valid &= formPageType.validate(); anyValue = true; } valid &= formApply.validate(); return anyValue && valid; } return false; } private DynamicForm createForm() { DynamicForm form = new DynamicForm(); form.setWrapItemTitles(false); // form.setAutoWidth(); form.setAutoHeight(); form.setBrowserSpellCheck(false); form.setSaveOnEnter(true); form.addSubmitValuesHandler(formSubmitHandler); return form; } private void createPageIndexUi() { formPageIndex = createForm(); allowPageIndexes = new CheckboxItem("fillPageIndexes", i18n.PageMetadataEditor_CheckboxPageIndices_Title()); allowPageIndexes.setStartRow(true); allowPageIndexes.setColSpan("*"); allowPageIndexes.setShowTitle(false); IntegerRangeValidator indexValidator = new IntegerRangeValidator(); indexValidator.setMin(0); indexValidator.setMax(1000000); indexStart = new IntegerItem("indexStart", i18n.PageMetadataEditor_IndexStartValue_Title()); indexStart.setRequired(true); indexStart.setValidators(indexValidator); allowPageIndexes.addChangedHandler(new DisableStateHandler(indexStart)); } private void createPageNumberUi() { formPageNumber = createForm(); allowPageNumbers = new CheckboxItem("fillPageNumbers", i18n.PageMetadataEditor_CheckboxPageNubers_Title()); allowPageNumbers.setStartRow(true); allowPageNumbers.setColSpan("*"); allowPageNumbers.setShowTitle(false); PageNumberChangeHandler pageNumberChangeHandler = new PageNumberChangeHandler(); integerStartValidator = new IntegerRangeValidator(); integerStartValidator.setMin(0); integerStartValidator.setMax(1000000); romanStartValidator = new RegExpValidator( "^[1-9][0-9]{0,6}$" + "|^[mM]{0,6}([cC][mM]|[cC][dD]|[dD]?[cC]{0,3})([xX][cC]|[xX][lL]|[lL]?[xX]{0,3})([iI][xX]|[iI][vV]|[vV]?[iI]{0,3})$"); alphabetStartValidator = new RegExpValidator("^[a-zA-Z]{0,6}$"); numberStartRequired = new RequiredIfValidator(new RequiredIfFunction() { @Override public boolean execute(FormItem formItem, Object value) { return true; } }); numberStart = new TextItem("numberStart", i18n.PageMetadataEditor_NumberStartValue_Title()); numberStart.addChangedHandler(pageNumberChangeHandler); prefix = new TextItem("prefix", i18n.PageMetadataEditor_NumberPrefix_Title()); prefix.setLength(20); prefix.addChangedHandler(pageNumberChangeHandler); suffix = new TextItem("suffix", i18n.PageMetadataEditor_NumberSuffix_Title()); suffix.setLength(20); suffix.addChangedHandler(pageNumberChangeHandler); integerIncrementValidator = new IntegerRangeValidator(); integerIncrementValidator.setMin(-1000); integerIncrementValidator.setMax(1000); numberIncrement = new IntegerItem("numberIncrement", i18n.PageMetadataEditor_NumberIncrement_Title()); numberIncrement.addChangedHandler(pageNumberChangeHandler); numberIncrement.setRequired(true); numberIncrement.setValidators(integerIncrementValidator); numberExample = new StaticTextItem("numberExample", i18n.PageMetadataEditor_NumberPreview_Title()); numberExample.setClipValue(true); numberExample.setWidth(120); // enforce clip value seriesType = new SelectItem("seriesType", i18n.PageMetadataEditor_NumberSeriesType_Title()); seriesType.setValueMap(ARABIC_SERIES, ROMAN_UPPER_SERIES, ROMAN_LOWER_SERIES, ALPHABET_UPPER_SERIES, ALPHABET_LOWER_SERIES); seriesType.setDefaultValue(ARABIC_SERIES); seriesType.setValue(ARABIC_SERIES); seriesType.addChangedHandler(new ChangedHandler() { @Override public void onChanged(ChangedEvent event) { setSequenceType(seriesType.getValueAsString()); } }); allowPageNumbers.addChangedHandler(new DisableStateHandler( prefix, numberStart, numberIncrement, suffix, seriesType, numberExample)); } /** * @see #setPageTypeValueMapId */ private void createPageTypeUi() { formPageType = createForm(); allowPageTypes = new CheckboxItem("fillPageTypes", i18n.PageMetadataEditor_CheckboxPageTypes_Title()); allowPageTypes.setStartRow(true); allowPageTypes.setColSpan("*"); allowPageTypes.setShowTitle(false); pageType = new SelectItem(ModsCustomDataSource.FIELD_PAGE_TYPE, i18n.PageForm_PageType_Title()); pageType.setDefaultValue(ModsCustomDataSource.getDefaultPageType()); pageType.setValue(ModsCustomDataSource.getDefaultPageType()); allowPageTypes.addChangedHandler(new DisableStateHandler(pageType)); } private void createApplyToUi() { formApply = createForm(); applyTo = new ComboBoxItem("applyTo", i18n.PageMetadataEditor_ApplyTo_Title()); applyTo.setPrompt(i18n.PageMetadataEditor_ApplyTo_Hint()); applyTo.setRequired(true); LinkedHashMap<String, String> vals = new LinkedHashMap<String, String>(); vals.put("1", i18n.PageMetadataEditor_ApplyToEachItem_Title()); for (int i = 2; i <= 20; i++) { String nth = String.valueOf(i); vals.put(nth, i18n.PageMetadataEditor_ApplyToEachNthItem_Title(nth)); } applyTo.setValueMap(vals); applyTo.setDefaultValue(1); applyTo.setValue(1); applyToValidator = new IntegerRangeValidator(); applyToValidator.setMin(1); applyToValidator.setMax(applyToMaxValue); applyTo.setValidators(new IsIntegerValidator(), applyToValidator); applyFromFirst = new CheckboxItem("applyFromFirst", i18n.PageMetadataEditor_ApplyFromFirst_Title()); applyFromFirst.setPrompt(i18n.PageMetadataEditor_ApplyFromFirst_Hint()); formApply.setItems(applyTo, applyFromFirst); applyTo.addChangedHandler(new ChangedHandler() { @Override public void onChanged(ChangedEvent event) { applyFromFirst.setDisabled("1".equals(applyTo.getValueAsString())); } }); } private void initPageIndex() { formPageIndex.clearValues(); allowPageIndexes.setValue(false); indexStart.setDisabled(!getAllowPageIndexes()); } private void initPageNumber() { formPageNumber.clearValues(); allowPageNumbers.setValue(false); boolean disablePageNumbers = !getAllowPageNumbers(); numberStart.setDisabled(disablePageNumbers); numberIncrement.setDisabled(disablePageNumbers); numberIncrement.setValue(1); seriesType.setDisabled(disablePageNumbers); prefix.setDisabled(disablePageNumbers); suffix.setDisabled(disablePageNumbers); } private void initPageType() { formPageType.clearValues(); allowPageTypes.setValue(false); pageType.setDisabled(!getAllowPageTypes()); } private void initApplyTo() { formApply.clearValues(); applyFromFirst.setValue(false); applyFromFirst.setDisabled(true); } /** * Resets form. */ public void initAll() { if (formPanel != null) { initPageIndex(); initPageNumber(); initPageType(); initApplyTo(); updatePageNumberValidators(seriesType.getValueAsString()); } } private Canvas createButtons() { SmartGwtMessages i18nSgwt = ClientUtils.createSmartGwtMessages(); IButton btnOk = new IButton(i18nSgwt.dialog_OkButtonTitle(), new com.smartgwt.client.widgets.events.ClickHandler() { @Override public void onClick(com.smartgwt.client.widgets.events.ClickEvent event) { boolean valid = validate(); if (valid) { window.hide(); if (windowCallback != null) { windowCallback.execute(true); } } } }); IButton btnCancel = new IButton(i18nSgwt.dialog_CancelButtonTitle(), new com.smartgwt.client.widgets.events.ClickHandler() { @Override public void onClick(com.smartgwt.client.widgets.events.ClickEvent event) { window.hide(); if (windowCallback != null) { windowCallback.execute(false); } } }); HStack btnLayout = new HStack(5); btnLayout.setAutoHeight(); btnLayout.setLayoutTopMargin(20); btnLayout.setLayoutAlign(Alignment.CENTER); btnLayout.setMembers(btnOk, btnCancel); return btnLayout; } public void showInWindow(BooleanCallback callback) { this.windowCallback = callback; if (window == null) { Canvas panelForm = getFormPanel(); panelForm.setPadding(5); Canvas panelButtons = createButtons(); panelButtons.setPadding(5); window = new Window(); window.setAutoSize(true); window.setAutoCenter(true); window.setIsModal(true); window.addItem(panelForm); window.addItem(panelButtons); window.setTitle(i18n.PageMetadataEditor_Window_Title()); window.setShowMinimizeButton(false); window.setShowModalMask(true); } initAll(); window.show(); formPageIndex.focusInItem(allowPageIndexes); } public boolean getAllowPageIndexes() { return allowPageIndexes.getValueAsBoolean(); } public boolean getAllowPageNumbers() { return allowPageNumbers.getValueAsBoolean(); } public boolean getAllowPageTypes() { return allowPageTypes.getValueAsBoolean(); } public boolean getApplyFromFirstItem() { return applyFromFirst.getValueAsBoolean(); } /** * Gets an increment to determine the next page of the selection. */ public int getApplyTo() { Integer val = getInt(applyTo.getValueAsString()); return Math.max(1, val == null ? 1 : val); } /** * Sets maximal value that is valid for the {@code Apply To} field. */ public void setMaxApplyTo(int size) { applyToMaxValue = size; applyToValidator.setMax(applyToMaxValue); } /** * Sets a page type value map. * @param typeValueMapId ID of a value map * @see ValueMapDataSource */ public void setPageTypeValueMapId(String typeValueMapId) { if (this.typeValueMapId == null ? typeValueMapId == null : this.typeValueMapId.equals(typeValueMapId)) { return ; } this.typeValueMapId = typeValueMapId; pageType.setOptionDataSource(ValueMapDataSource.getInstance().getOptionDataSource(typeValueMapId)); pageType.setValueField(BundleValue.KEY); pageType.setDisplayField(BundleValue.VALUE); } public Integer getIndexStart() { return getUnsignedInteger(indexStart); } public String getNumberStart() { return getNormalizedString(numberStart.getValueAsString()); } public Iterator<String> getSequence() { String type = seriesType.getValueAsString(); String start = getNumberStart(); Integer increment = getIncrement(); if (start == null || increment == null || !numberStart.validate() || !numberIncrement.validate()) { return null; } if (ARABIC_SERIES.equals(type)) { Integer arabicStart = getInt(start); return arabicStart == null ? null : Series.arabic(arabicStart, increment).iterator(); } else if (ROMAN_UPPER_SERIES.equals(type) || ROMAN_LOWER_SERIES.equals(type)) { boolean upperCase = ROMAN_UPPER_SERIES.equals(type); if (Series.validRoman(start)) { return Series.roman(start, increment, upperCase).iterator(); } else { Integer romanStart = getPositiveInt(start); return romanStart == null ? null : Series.roman(romanStart, increment, upperCase).iterator(); } } else if (ALPHABET_UPPER_SERIES.equals(type) || ALPHABET_LOWER_SERIES.equals(type)) { boolean upperCase = ALPHABET_UPPER_SERIES.equals(type); if (!Series.validAlphabet(start) || Math.abs(increment) > 26) { return null; } return Series.alphabet(start, increment, upperCase).iterator(); } throw new IllegalStateException(type); } private Integer getIncrement() { return getInt(getNormalizedString(numberIncrement.getValueAsString())); } private static Integer getInt(String value) { if (value == null) { return null; } try { return Integer.decode(value); } catch (NumberFormatException ex) { return null; } } private static Integer getPositiveInt(String value) { Integer result = getInt(value); return result == null || result < 1 ? null : result; } /** * Workaround for {@link IntegerItem#getValueAsInteger() } that fails in * the development mode. (SmartGWT 2.5) */ private static Integer getUnsignedInteger(IntegerItem item) { Integer result = null; String value = getNormalizedString(item.getValueAsString()); if (value != null) { try { result = Integer.parseInt(value); } catch (NumberFormatException ex) { } } return (result == null || result < 0) ? null : result; } public String getPrefix() { String prefixValue = prefix.getValueAsString(); return getNormalizedPrefix(prefixValue); } public String getSuffix() { String suffixValue = suffix.getValueAsString(); return getNormalizedPrefix(suffixValue); } public String getPageType() { return pageType.getValueAsString(); } private static String getNormalizedPrefix(String s) { if (s != null) { if (s.trim().isEmpty()) { s = null; } } return s; } private static String getNormalizedString(String s) { if (s != null) { s = s.trim(); if (s.isEmpty()) { s = null; } } return s; } private void setPreview() { String prefixValue = getPrefix(); String suffixValue = getSuffix(); Iterator<String> sequence = getSequence(); StringBuilder sequenceItem = new StringBuilder(); for (int i = 0; i < 3; i++) { if (prefixValue != null) { sequenceItem.append(prefixValue); } if (sequence != null) { sequenceItem.append(sequence.next()); } else { // sequenceItem.append("<err>"); } if (suffixValue != null) { sequenceItem.append(suffixValue); } if (sequenceItem.length() > 0) { sequenceItem.append(", "); } else { break; } } if (sequenceItem.length() > 0) { sequenceItem.append("..."); } String example = SafeHtmlUtils.htmlEscapeAllowEntities(sequenceItem.toString()); numberExample.setValue(example); numberExample.setPrompt(example); } private void updatePageNumberValidators(String seriesType) { numberExample.setPrompt(seriesType); if (ARABIC_SERIES.equals(seriesType)) { numberStart.setValidators(new IsIntegerValidator(), integerStartValidator, numberStartRequired); integerIncrementValidator.setMin(-1000); integerIncrementValidator.setMax(1000); } else if (ROMAN_LOWER_SERIES.equals(seriesType) || ROMAN_UPPER_SERIES.equals(seriesType)) { numberStart.setValidators(romanStartValidator, numberStartRequired); integerIncrementValidator.setMin(-1000); integerIncrementValidator.setMax(1000); } else { numberStart.setValidators(alphabetStartValidator, numberStartRequired); integerIncrementValidator.setMin(-26); integerIncrementValidator.setMax(26); } } private void setSequenceType(String seriesType) { updatePageNumberValidators(seriesType); setPreview(); } private final class PageNumberChangeHandler implements ChangedHandler { @Override public void onChanged(ChangedEvent event) { setPreview(); } } private static final class DisableStateHandler implements ChangedHandler { private final FormItem[] items; public DisableStateHandler(FormItem... items) { this.items = items; } @Override public void onChanged(ChangedEvent event) { boolean enabled = (Boolean) event.getValue(); for (FormItem item : items) { item.setDisabled(!enabled); } if (enabled && items.length > 0) { items[0].focusInItem(); } } } }