/* * See the NOTICE file distributed with this work for additional * information regarding copyright ownership. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.xwiki.gwt.wysiwyg.client.plugin.image.ui; import org.xwiki.gwt.user.client.FocusCommand; import org.xwiki.gwt.user.client.StringUtils; import org.xwiki.gwt.user.client.ui.wizard.AbstractInteractiveWizardStep; import org.xwiki.gwt.user.client.ui.wizard.NavigationListener; import org.xwiki.gwt.user.client.ui.wizard.NavigationListener.NavigationDirection; import org.xwiki.gwt.user.client.ui.wizard.NavigationListenerCollection; import org.xwiki.gwt.user.client.ui.wizard.SourcesNavigationEvents; import org.xwiki.gwt.wysiwyg.client.Strings; import org.xwiki.gwt.wysiwyg.client.plugin.image.ImageConfig; import org.xwiki.gwt.wysiwyg.client.wiki.EntityLink; import org.xwiki.gwt.wysiwyg.client.wiki.ResourceReference.ResourceType; import org.xwiki.gwt.wysiwyg.client.wiki.URIReference; import com.google.gwt.core.client.Scheduler; import com.google.gwt.event.dom.client.KeyCodes; import com.google.gwt.event.dom.client.KeyUpEvent; import com.google.gwt.event.dom.client.KeyUpHandler; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.InlineLabel; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.Panel; import com.google.gwt.user.client.ui.TextBox; /** * Allows the user to select an external image by specifying its URL. * * @version $Id: 2232db07ac538d1e972cd3c0225b34371343cb5b $ */ public class URLImageSelectorWizardStep extends AbstractInteractiveWizardStep implements SourcesNavigationEvents, KeyUpHandler { /** * The style of the fields under error. */ private static final String FIELD_ERROR_STYLE = "xErrorField"; /** * The character sequence that delimits the URL protocol from the URL body. */ private static final String PROTOCOL_DELIMITER = "://"; /** * The entity link managed by the wizard step. This wizard step updates image reference. */ private EntityLink<ImageConfig> entityLink; /** * Collection of {@link NavigationListener}s, to be notified by navigation events from this step. Used to handle * default buttons in this wizard step, by firing next event whenever enter key is hit in the step form. */ private final NavigationListenerCollection navigationListeners = new NavigationListenerCollection(); /** * The text box used to input the image source in the form of an UR. */ private final TextBox source = new TextBox(); /** * The label used to display source validation errors. */ private final Label sourceValidationError = new Label(Strings.INSTANCE.imageExternalLocationNotSpecifiedError()); /** * Creates a new wizard step that allows the user to select an external image by specifying its URL. */ public URLImageSelectorWizardStep() { setStepTitle(Strings.INSTANCE.imageSelectImageTitle()); InlineLabel mandatoryLabel = new InlineLabel(Strings.INSTANCE.mandatory()); mandatoryLabel.addStyleName("xMandatory"); Panel sourceLabel = new FlowPanel(); sourceLabel.setStyleName("xInfoLabel"); sourceLabel.add(new InlineLabel(Strings.INSTANCE.imageExternalLocationLabel())); sourceLabel.add(mandatoryLabel); display().add(sourceLabel); Label sourceHelpLabel = new Label(Strings.INSTANCE.imageExternalLocationHelpLabel()); sourceHelpLabel.setStyleName("xHelpLabel"); display().add(sourceHelpLabel); sourceValidationError.addStyleName("xErrorMsg"); sourceValidationError.setVisible(false); display().add(sourceValidationError); source.setTitle(Strings.INSTANCE.imageExternalLocationLabel()); source.addKeyUpHandler(this); display().add(source); display().addStyleName("xExternalImage"); } @Override public Object getResult() { return entityLink; } @Override @SuppressWarnings("unchecked") public void init(Object data, AsyncCallback< ? > cb) { entityLink = (EntityLink<ImageConfig>) data; String imageURL = entityLink.getData().getUrl(); if (!StringUtils.isEmpty(imageURL) && imageURL.contains(PROTOCOL_DELIMITER)) { // In case the image URL doesn't include the protocol (which can happen if the user selects an internal // image and then opts for an external image) we leave the source input blank to force the user to enter a // new image location. source.setText(imageURL); } Scheduler.get().scheduleDeferred(new FocusCommand(source)); cb.onSuccess(null); } @Override public void onCancel() { entityLink = null; hideValidationErrors(); } @Override public void onSubmit(AsyncCallback<Boolean> async) { if (validate()) { updateResult(); async.onSuccess(true); } else { async.onSuccess(false); } } /** * Validates all input fields from this wizard step and focuses the first field with an illegal value. * * @return {@code true} if the current input values are valid, {@code false} otherwise */ private boolean validate() { hideValidationErrors(); if (source.getText().trim().length() == 0) { sourceValidationError.setVisible(true); source.addStyleName(FIELD_ERROR_STYLE); Scheduler.get().scheduleDeferred(new FocusCommand(source)); return false; } return true; } /** * Updates the result object returned by this wizard step and notifies the call-back object. */ private void updateResult() { String imageURL = source.getText().trim(); if (!imageURL.contains(PROTOCOL_DELIMITER)) { imageURL = "http://" + imageURL; } entityLink.getDestination().setType(ResourceType.URL); entityLink.getDestination().setEntityReference(new URIReference(imageURL).getEntityReference()); entityLink.getData().setReference(null); entityLink.getData().setUrl(imageURL); } /** * Hides the validation errors. */ private void hideValidationErrors() { sourceValidationError.setVisible(false); source.removeStyleName(FIELD_ERROR_STYLE); } @Override public void addNavigationListener(NavigationListener listener) { navigationListeners.add(listener); } @Override public void removeNavigationListener(NavigationListener listener) { navigationListeners.remove(listener); } @Override public void onKeyUp(KeyUpEvent event) { if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER) { navigationListeners.fireNavigationEvent(NavigationDirection.NEXT); } } }