001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com)
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * For further information about Alkacon Software, please see the
018 * company website: http://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: http://www.opencms.org
022 *
023 * You should have received a copy of the GNU Lesser General Public
024 * License along with this library; if not, write to the Free Software
025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
026 */
027
028package org.opencms.ade.galleries.client.ui;
029
030import org.opencms.ade.galleries.client.CmsGalleryConfigurationJSO;
031import org.opencms.ade.galleries.client.CmsGalleryController;
032import org.opencms.ade.galleries.client.I_CmsGalleryWidgetHandler;
033import org.opencms.ade.galleries.client.Messages;
034import org.opencms.ade.galleries.client.preview.CmsCroppingParamBean;
035import org.opencms.ade.galleries.client.ui.css.I_CmsLayoutBundle;
036import org.opencms.ade.galleries.shared.CmsGalleryActionInfo;
037import org.opencms.ade.galleries.shared.CmsResultItemBean;
038import org.opencms.ade.galleries.shared.I_CmsGalleryConfiguration;
039import org.opencms.ade.galleries.shared.rpc.I_CmsGalleryService;
040import org.opencms.ade.galleries.shared.rpc.I_CmsGalleryServiceAsync;
041import org.opencms.ade.upload.client.I_CmsUploadContext;
042import org.opencms.ade.upload.client.ui.CmsDialogUploadButtonHandler;
043import org.opencms.file.CmsResource;
044import org.opencms.gwt.client.CmsCoreProvider;
045import org.opencms.gwt.client.I_CmsHasInit;
046import org.opencms.gwt.client.rpc.CmsRpcAction;
047import org.opencms.gwt.client.ui.CmsListItemWidget;
048import org.opencms.gwt.client.ui.CmsPushButton;
049import org.opencms.gwt.client.ui.I_CmsAutoHider;
050import org.opencms.gwt.client.ui.I_CmsButton;
051import org.opencms.gwt.client.ui.I_CmsButton.ButtonStyle;
052import org.opencms.gwt.client.ui.I_CmsButton.Size;
053import org.opencms.gwt.client.ui.input.CmsSimpleTextBox;
054import org.opencms.gwt.client.ui.input.I_CmsFormWidget;
055import org.opencms.gwt.client.ui.input.form.CmsWidgetFactoryRegistry;
056import org.opencms.gwt.client.ui.input.form.I_CmsFormWidgetFactory;
057import org.opencms.gwt.client.ui.input.upload.CmsFileInfo;
058import org.opencms.gwt.client.ui.input.upload.CmsUploadButton;
059import org.opencms.gwt.client.util.CmsClientStringUtil;
060import org.opencms.gwt.client.util.CmsDomUtil;
061import org.opencms.gwt.client.util.CmsEmbeddedDialogHandler;
062import org.opencms.util.CmsStringUtil;
063import org.opencms.util.CmsUUID;
064
065import java.util.ArrayList;
066import java.util.HashMap;
067import java.util.List;
068import java.util.Map;
069
070import com.google.common.base.Optional;
071import com.google.common.base.Supplier;
072import com.google.gwt.core.client.GWT;
073import com.google.gwt.core.client.JavaScriptObject;
074import com.google.gwt.core.client.JsArray;
075import com.google.gwt.dom.client.DivElement;
076import com.google.gwt.dom.client.Element;
077import com.google.gwt.dom.client.Style.Unit;
078import com.google.gwt.event.dom.client.BlurEvent;
079import com.google.gwt.event.dom.client.ClickEvent;
080import com.google.gwt.event.dom.client.ClickHandler;
081import com.google.gwt.event.dom.client.FocusEvent;
082import com.google.gwt.event.dom.client.FocusHandler;
083import com.google.gwt.event.dom.client.HasFocusHandlers;
084import com.google.gwt.event.logical.shared.CloseEvent;
085import com.google.gwt.event.logical.shared.CloseHandler;
086import com.google.gwt.event.logical.shared.HasResizeHandlers;
087import com.google.gwt.event.logical.shared.HasValueChangeHandlers;
088import com.google.gwt.event.logical.shared.OpenEvent;
089import com.google.gwt.event.logical.shared.OpenHandler;
090import com.google.gwt.event.logical.shared.ResizeEvent;
091import com.google.gwt.event.logical.shared.ResizeHandler;
092import com.google.gwt.event.logical.shared.ValueChangeEvent;
093import com.google.gwt.event.logical.shared.ValueChangeHandler;
094import com.google.gwt.event.shared.HandlerRegistration;
095import com.google.gwt.uibinder.client.UiBinder;
096import com.google.gwt.uibinder.client.UiField;
097import com.google.gwt.uibinder.client.UiHandler;
098import com.google.gwt.user.client.DOM;
099import com.google.gwt.user.client.Timer;
100import com.google.gwt.user.client.rpc.ServiceDefTarget;
101import com.google.gwt.user.client.ui.Composite;
102import com.google.gwt.user.client.ui.FlowPanel;
103import com.google.gwt.user.client.ui.HTMLPanel;
104import com.google.gwt.user.client.ui.IsWidget;
105import com.google.gwt.user.client.ui.Label;
106
107/**
108 * A widget for selecting a resource from an ADE gallery dialog.<p>
109 *
110 * @since 8.0.0
111 */
112public class CmsGalleryField extends Composite
113implements I_CmsFormWidget, I_CmsHasInit, HasValueChangeHandlers<String>, HasResizeHandlers, HasFocusHandlers {
114
115    /**
116     * The UI Binder interface for this widget.<p>
117     */
118    protected interface I_CmsGalleryFieldUiBinder extends UiBinder<HTMLPanel, CmsGalleryField> {
119        // binder interface
120    }
121
122    /**
123     * Handler to fire resize event on resource info widget open/close.<p>
124     */
125    protected class OpenCloseHandler implements CloseHandler<CmsListItemWidget>, OpenHandler<CmsListItemWidget> {
126
127        /**
128         * @see com.google.gwt.event.logical.shared.CloseHandler#onClose(com.google.gwt.event.logical.shared.CloseEvent)
129         */
130        @Override
131        public void onClose(CloseEvent<CmsListItemWidget> event) {
132
133            fireResize();
134        }
135
136        /**
137         * @see com.google.gwt.event.logical.shared.OpenHandler#onOpen(com.google.gwt.event.logical.shared.OpenEvent)
138         */
139        @Override
140        public void onOpen(OpenEvent<CmsListItemWidget> event) {
141
142            fireResize();
143        }
144    }
145
146    /** The widget type. */
147    public static final String WIDGET_TYPE = "gallery";
148
149    /** The ui binder for this widget. */
150    private static I_CmsGalleryFieldUiBinder uibinder = GWT.create(I_CmsGalleryFieldUiBinder.class);
151
152    /** The gallery configuration. */
153    protected I_CmsGalleryConfiguration m_configuration;
154
155    /** The scale parameters from popup. */
156    protected CmsCroppingParamBean m_croppingParam;
157
158    /** The fading element. */
159    @UiField
160    protected Label m_fader;
161
162    /** The DIV carrying the input field. */
163    @UiField
164    protected DivElement m_fieldBox;
165
166    /** The image preview element. */
167    @UiField
168    protected DivElement m_imagePreview;
169
170    /** The main panel. */
171    protected HTMLPanel m_main;
172
173    /** The button to to open the selection. */
174    @UiField
175    protected CmsPushButton m_opener;
176
177    /** The gallery pop-up. */
178    protected CmsGalleryPopup m_popup;
179
180    /** The resource info panel. */
181    @UiField
182    protected FlowPanel m_resourceInfoPanel;
183
184    /** The special upload button. */
185    @UiField(provided = true)
186    protected CmsPushButton m_specialUploadButton;
187
188    /** The textbox containing the currently selected path. */
189    @UiField
190    protected CmsSimpleTextBox m_textbox;
191
192    /** The upload button. */
193    @UiField(provided = true)
194    protected CmsUploadButton m_uploadButton;
195
196    /** The upload drop zone. */
197    protected Element m_uploadDropZone;
198
199    /** The upload target folder. */
200    String m_uploadTarget;
201
202    /** Flag indicating uploads are allowed. */
203    private boolean m_allowUploads;
204
205    /** The gallery service instance. */
206    private I_CmsGalleryServiceAsync m_gallerySvc;
207
208    /** The has image flag. */
209    private boolean m_hasImage;
210
211    /** The info timer instance. */
212    private Timer m_infoTimer;
213
214    /** The previous field value. */
215    private String m_previousValue;
216
217    /** The upload action. */
218    private String m_uploadAction;
219
220    /**
221     * Constructs a new gallery widget.<p>
222     *
223     * @param configuration the gallery configuration
224     * @param allowUploads states if the upload button should be enabled for this widget
225     */
226    public CmsGalleryField(I_CmsGalleryConfiguration configuration, boolean allowUploads) {
227
228        CmsDialogUploadButtonHandler buttonHandler = new CmsDialogUploadButtonHandler(
229            new Supplier<I_CmsUploadContext>() {
230
231                @Override
232                public I_CmsUploadContext get() {
233
234                    return new I_CmsUploadContext() {
235
236                        @Override
237                        public void onUploadFinished(List<String> uploadedFiles) {
238
239                            if ((uploadedFiles != null) && !uploadedFiles.isEmpty()) {
240                                setValue(m_uploadTarget + uploadedFiles.iterator().next(), true);
241                            }
242                        }
243
244                    };
245                }
246            });
247        buttonHandler.setIsTargetRootPath(false);
248        m_uploadButton = new CmsUploadButton(buttonHandler);
249        m_uploadButton.setText(null);
250        m_uploadButton.setTitle(
251            Messages.get().key(Messages.GUI_GALLERY_UPLOAD_TITLE_1, configuration.getUploadFolder()));
252        m_uploadButton.setButtonStyle(ButtonStyle.FONT_ICON, null);
253        m_uploadButton.setImageClass(I_CmsButton.UPLOAD);
254        m_uploadButton.setSize(Size.small);
255        m_uploadButton.removeStyleName(I_CmsLayoutBundle.INSTANCE.generalCss().cornerAll());
256        m_uploadButton.getElement().setTabIndex(-1);
257
258        m_specialUploadButton = createSpecialUploadButton();
259
260        m_main = uibinder.createAndBindUi(this);
261        initWidget(m_main);
262        m_allowUploads = allowUploads;
263        if (m_allowUploads) {
264            m_fieldBox.addClassName(I_CmsLayoutBundle.INSTANCE.galleryFieldCss().hasUpload());
265        }
266        m_configuration = configuration;
267        I_CmsLayoutBundle.INSTANCE.galleryFieldCss().ensureInjected();
268        m_opener.setButtonStyle(ButtonStyle.FONT_ICON, null);
269        m_opener.setImageClass(I_CmsButton.GALLERY);
270        m_opener.setSize(Size.small);
271        m_opener.setHideFromTabNav(true);
272    }
273
274    /**
275     * Initializes this class.<p>
276     */
277    public static void initClass() {
278
279        // registers a factory for creating new instances of this widget
280        CmsWidgetFactoryRegistry.instance().registerFactory(WIDGET_TYPE, new I_CmsFormWidgetFactory() {
281
282            /**
283             * @see org.opencms.gwt.client.ui.input.form.I_CmsFormWidgetFactory#createWidget(java.util.Map, com.google.common.base.Optional)
284             */
285            @Override
286            public I_CmsFormWidget createWidget(Map<String, String> widgetParams, Optional<String> defaultValue) {
287
288                String config = widgetParams.get("configuration");
289                if (CmsStringUtil.isEmptyOrWhitespaceOnly(config)) {
290                    config = "{}";
291                }
292                CmsGalleryConfigurationJSO conf = CmsGalleryConfigurationJSO.parseConfiguration(config);
293                CmsGalleryField galleryField = new CmsGalleryField(conf, false);
294                return galleryField;
295            }
296        });
297    }
298
299    /**
300     * Adds a style name to the DIV carrying the input field.<p>
301     *
302     * @param styleName the style name to add
303     */
304    public void addFieldStyleName(String styleName) {
305
306        m_fieldBox.addClassName(styleName);
307    }
308
309    /**
310     * @see com.google.gwt.event.dom.client.HasFocusHandlers#addFocusHandler(com.google.gwt.event.dom.client.FocusHandler)
311     */
312    @Override
313    public HandlerRegistration addFocusHandler(FocusHandler handler) {
314
315        return addDomHandler(handler, FocusEvent.getType());
316    }
317
318    /**
319     * @see com.google.gwt.event.logical.shared.HasResizeHandlers#addResizeHandler(com.google.gwt.event.logical.shared.ResizeHandler)
320     */
321    @Override
322    public HandlerRegistration addResizeHandler(ResizeHandler handler) {
323
324        return addHandler(handler, ResizeEvent.getType());
325    }
326
327    /**
328     * @see com.google.gwt.event.logical.shared.HasValueChangeHandlers#addValueChangeHandler(com.google.gwt.event.logical.shared.ValueChangeHandler)
329     */
330    @Override
331    public HandlerRegistration addValueChangeHandler(ValueChangeHandler<String> handler) {
332
333        return addHandler(handler, ValueChangeEvent.getType());
334    }
335
336    /**
337     * @see org.opencms.gwt.client.ui.input.I_CmsFormWidget#getApparentValue()
338     */
339    @Override
340    public String getApparentValue() {
341
342        return getFormValueAsString();
343    }
344
345    /**
346     * @see org.opencms.gwt.client.ui.input.I_CmsFormWidget#getFieldType()
347     */
348    @Override
349    public FieldType getFieldType() {
350
351        return FieldType.STRING;
352    }
353
354    /**
355     * @see org.opencms.gwt.client.ui.input.I_CmsFormWidget#getFormValue()
356     */
357    @Override
358    public Object getFormValue() {
359
360        return getFormValueAsString();
361    }
362
363    /**
364     * @see org.opencms.gwt.client.ui.input.I_CmsFormWidget#getFormValueAsString()
365     */
366    @Override
367    public String getFormValueAsString() {
368
369        return m_textbox.getValue();
370    }
371
372    /**
373     * Returns the gallery popup.<p>
374     *
375     * @return the gallery popup
376     */
377    public CmsGalleryPopup getPopup() {
378
379        return m_popup;
380
381    }
382
383    /**
384     * @see org.opencms.gwt.client.ui.input.I_CmsFormWidget#isEnabled()
385     */
386    @Override
387    public boolean isEnabled() {
388
389        return m_textbox.isEnabled();
390    }
391
392    /**
393     * @see org.opencms.gwt.client.ui.input.I_CmsFormWidget#reset()
394     */
395    @Override
396    public void reset() {
397
398        setFormValueAsString("");
399    }
400
401    /**
402     * @see org.opencms.gwt.client.ui.input.I_CmsFormWidget#setAutoHideParent(org.opencms.gwt.client.ui.I_CmsAutoHider)
403     */
404    @Override
405    public void setAutoHideParent(I_CmsAutoHider autoHideParent) {
406
407        // do nothing
408    }
409
410    /**
411     * Sets the upload drop zone element.<p>
412     *
413     * @param dropZone the upload drop zone element
414     */
415    public void setDropZoneElement(Element dropZone) {
416
417        if (m_allowUploads && (dropZone != null) && (m_uploadDropZone == null)) {
418            m_uploadDropZone = dropZone;
419            initUploadZone(m_uploadDropZone);
420            m_uploadDropZone.setTitle(
421                org.opencms.ade.upload.client.Messages.get().key(
422                    org.opencms.ade.upload.client.Messages.GUI_UPLOAD_DRAG_AND_DROP_ENABLED_0));
423            m_uploadDropZone.addClassName(I_CmsLayoutBundle.INSTANCE.galleryFieldCss().uploadDropZone());
424        }
425    }
426
427    /**
428     * @see org.opencms.gwt.client.ui.input.I_CmsFormWidget#setEnabled(boolean)
429     */
430    @Override
431    public void setEnabled(boolean enabled) {
432
433        m_textbox.setEnabled(enabled);
434    }
435
436    /**
437     * @see org.opencms.gwt.client.ui.input.I_CmsFormWidget#setErrorMessage(java.lang.String)
438     */
439    @Override
440    public void setErrorMessage(String errorMessage) {
441
442        // do nothing
443    }
444
445    /**
446     * @see org.opencms.gwt.client.ui.input.I_CmsFormWidget#setFormValueAsString(java.lang.String)
447     */
448    @Override
449    public void setFormValueAsString(String value) {
450
451        setValue(value, false);
452    }
453
454    /**
455     * Sets the gallery opener button title.<p>
456     *
457     * @param openerTitle the gallery opener button title
458     */
459    public void setGalleryOpenerTitle(String openerTitle) {
460
461        m_opener.setTitle(openerTitle);
462    }
463
464    /**
465     * Sets the has image flag.<p>
466     *
467     * @param hasImage the has image flag
468     **/
469    public void setHasImage(boolean hasImage) {
470
471        m_hasImage = hasImage;
472    }
473
474    /**
475     * Sets the name of the input field.<p>
476     *
477     * @param name of the input field
478     * */
479    public void setName(String name) {
480
481        m_textbox.setName(name);
482
483    }
484
485    /**
486     * Adds a widget to the main panel.<p>
487     *
488     * @param widget the widget to add
489     */
490    protected void addToMain(IsWidget widget) {
491
492        m_main.add(widget);
493    }
494
495    /**
496     * Fires the value change event if the value has changed.<p>
497     *
498     * @param force <code>true</code> to force firing the event in any case
499     */
500    protected void fireChange(boolean force) {
501
502        String value = getFormValueAsString();
503        if (force || !value.equals(m_previousValue)) {
504            m_previousValue = value;
505            ValueChangeEvent.fire(this, value);
506        }
507    }
508
509    /**
510     * Fires the resize event for this widget.<p>
511     */
512    protected void fireResize() {
513
514        ResizeEvent.fire(this, getElement().getOffsetWidth(), getElement().getOffsetHeight());
515    }
516
517    /**
518     * Returns the currently set resource path.<p>
519     *
520     * @return the currently set resource path
521     */
522    protected String getCurrentElement() {
523
524        return getFormValueAsString();
525    }
526
527    /**
528     * Returns the gallery service instance.<p>
529     *
530     * @return the gallery service instance
531     */
532    protected I_CmsGalleryServiceAsync getGalleryService() {
533
534        if (m_gallerySvc == null) {
535            m_gallerySvc = GWT.create(I_CmsGalleryService.class);
536            String serviceUrl = CmsCoreProvider.get().link("org.opencms.ade.galleries.CmsGalleryService.gwt");
537            ((ServiceDefTarget)m_gallerySvc).setServiceEntryPoint(serviceUrl);
538        }
539        return m_gallerySvc;
540    }
541
542    /**
543     * Checks if drag/drop should be enabled.
544     *
545     * @return true if drag/drop should be enabled
546     */
547    protected boolean isDndEnabled() {
548
549        return m_uploadAction == null;
550    }
551
552    /**
553     * Handles the focus event on the opener.<p>
554     *
555     * @param event  the focus event
556     */
557    @UiHandler("m_textbox")
558    protected void onFocusTextbox(FocusEvent event) {
559
560        CmsDomUtil.fireFocusEvent(this);
561    }
562
563    /**
564     * Internal method which opens the gallery dialog.<p>
565     */
566    protected void openGalleryDialog() {
567
568        if (m_popup == null) {
569            m_popup = createPopup();
570            m_popup.center();
571        } else {
572            if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(getCurrentElement())) {
573                m_popup.searchElement(getCurrentElement());
574            } else {
575                m_popup.center();
576            }
577        }
578    }
579
580    /**
581     * Removes the given widget from the main panel.<p>
582     *
583     * @param widget the widget to remove
584     *
585     * @return <code>true</code> if the widget was a child of the main panel
586     */
587    protected boolean removeFromMain(IsWidget widget) {
588
589        return m_main.remove(widget);
590    }
591
592    /**
593     * Sets the image preview.<p>
594     *
595     * @param realPath the actual image path
596     * @param imagePath the image path
597     */
598    protected void setImagePreview(String realPath, String imagePath) {
599
600        if ((m_croppingParam == null) || !getFormValueAsString().contains(m_croppingParam.toString())) {
601            m_croppingParam = CmsCroppingParamBean.parseImagePath(getFormValueAsString());
602        }
603        CmsCroppingParamBean restricted;
604        int marginTop = 0;
605        if (m_croppingParam.getScaleParam().isEmpty()) {
606            imagePath += "?__scale=w:165,h:114,t:1,c:white,r:0";
607        } else {
608            restricted = m_croppingParam.getRestrictedSizeParam(114, 165);
609            imagePath += "?" + restricted.toString();
610            marginTop = (114 - restricted.getResultingHeight()) / 2;
611        }
612        Element image = DOM.createImg();
613        image.setAttribute("src", imagePath);
614        image.getStyle().setMarginTop(marginTop, Unit.PX);
615        if (CmsClientStringUtil.checkIsPathOrLinkToSvg(realPath)) {
616            image.getStyle().setWidth(100, Unit.PCT);
617            image.getStyle().setHeight(100, Unit.PCT);
618            image.getStyle().setProperty("objectFit", "contain");
619        }
620        m_imagePreview.setInnerHTML("");
621        m_imagePreview.appendChild(image);
622    }
623
624    /**
625     * Sets the widget value.<p>
626     *
627     * @param value the value to set
628     * @param fireEvent if the change event should be fired
629     */
630    protected void setValue(String value, boolean fireEvent) {
631
632        m_textbox.setValue(value);
633        updateUploadTarget(CmsResource.getFolderPath(value));
634        updateResourceInfo(value);
635        m_previousValue = value;
636        if (fireEvent) {
637            fireChange(true);
638        }
639    }
640
641    /**
642     * Sets the widget value. To be called from the gallery dialog.<p>
643     *
644     * @param resourcePath the selected resource path
645     * @param structureId the resource structure id
646     * @param croppingParameter the selected cropping
647     */
648    protected void setValueFromGallery(
649        String resourcePath,
650        CmsUUID structureId,
651        CmsCroppingParamBean croppingParameter) {
652
653        m_croppingParam = croppingParameter;
654        String path = resourcePath;
655        // in case of an image check the cropping parameter
656        if ((m_croppingParam != null) && (m_croppingParam.isCropped() || m_croppingParam.isScaled())) {
657            path += "?" + m_croppingParam.toString();
658        }
659        setValue(path, true);
660        m_popup.hide();
661    }
662
663    /**
664     * Updates the upload target folder path.<p>
665     *
666     * @param uploadTarget the upload target folder
667     */
668    protected void updateUploadTarget(String uploadTarget) {
669
670        m_uploadAction = null;
671        if (CmsStringUtil.isEmptyOrWhitespaceOnly(uploadTarget)) {
672            m_uploadTarget = m_configuration.getUploadFolder();
673        } else {
674            m_uploadTarget = uploadTarget;
675        }
676        if (CmsStringUtil.isEmptyOrWhitespaceOnly(m_uploadTarget)) {
677            setSpecialUpload(false);
678            // disable the upload button as no target folder is available
679            m_uploadButton.disable(
680                org.opencms.ade.upload.client.Messages.get().key(
681                    org.opencms.ade.upload.client.Messages.GUI_UPLOAD_BUTTON_NO_TARGET_0));
682        } else {
683            CmsRpcAction<CmsGalleryActionInfo> action = new CmsRpcAction<CmsGalleryActionInfo>() {
684
685                @Override
686                public void execute() {
687
688                    start(0, false);
689                    CmsGalleryController.getGalleryService().getGalleryActionInfo(uploadTarget, this);
690
691                }
692
693                @SuppressWarnings("synthetic-access")
694                @Override
695                protected void onResponse(CmsGalleryActionInfo result) {
696
697                    stop(false);
698                    if ((result == null) || (result.getUploadAction() == null)) {
699                        setSpecialUpload(false);
700                        // make sure the upload button is available
701                        m_uploadButton.enable();
702                        ((CmsDialogUploadButtonHandler)m_uploadButton.getButtonHandler()).setTargetFolder(
703                            m_uploadTarget);
704                        m_uploadButton.updateFileInput();
705                        m_uploadButton.setTitle(
706                            Messages.get().key(Messages.GUI_GALLERY_UPLOAD_TITLE_1, m_uploadTarget));
707
708                    } else {
709                        m_uploadAction = result.getUploadAction();
710                        setSpecialUpload(true);
711                    }
712                }
713            };
714            action.execute();
715
716        }
717    }
718
719    /**
720     * Clears the info timer.<p>
721     */
722    void clearInfoTimer() {
723
724        m_infoTimer = null;
725    }
726
727    /**
728     * Displays the resource info.<p>
729     *
730     * @param info the resource info
731     */
732    void displayResourceInfo(CmsResultItemBean info) {
733
734        if (m_hasImage) {
735            setImagePreview(info.getPath(), info.getViewLink());
736            m_resourceInfoPanel.add(new CmsImageInfo(info, info.getDimension()));
737        } else {
738            CmsListItemWidget widget = new CmsListItemWidget(info);
739            OpenCloseHandler handler = new OpenCloseHandler();
740            widget.addCloseHandler(handler);
741            widget.addOpenHandler(handler);
742            m_resourceInfoPanel.add(widget);
743            int width = m_resourceInfoPanel.getOffsetWidth();
744            if (width > 0) {
745                widget.truncate("STANDARD", width);
746            }
747        }
748        fireResize();
749    }
750
751    /**
752     * On text box blur.<p>
753     *
754     * @param event the event
755     */
756    @UiHandler("m_textbox")
757    void onBlur(BlurEvent event) {
758
759        setFaded((m_textbox.getValue().length() * 6.88) > m_textbox.getOffsetWidth());
760        setTitle(m_textbox.getValue());
761    }
762
763    /**
764     * Handles styling changes on drag out.<p>
765     */
766    void onDragOut() {
767
768        m_uploadDropZone.removeClassName(I_CmsLayoutBundle.INSTANCE.galleryFieldCss().dropZoneHover());
769    }
770
771    /**
772     * Handles styling changes on drag over.<p>
773     */
774    void onDragOver() {
775
776        if (m_uploadTarget != null) {
777            m_uploadDropZone.addClassName(I_CmsLayoutBundle.INSTANCE.galleryFieldCss().dropZoneHover());
778        }
779    }
780
781    /**
782     * On fader click.<p>
783     *
784     * @param event the event
785     */
786    @UiHandler("m_fader")
787    void onFaiderClick(ClickEvent event) {
788
789        m_textbox.setFocus(true);
790    }
791
792    /**
793     * On opener click.<p>
794     *
795     * @param event the event
796     */
797    @UiHandler("m_opener")
798    void onOpenerClick(ClickEvent event) {
799
800        m_opener.clearHoverState();
801        openGalleryDialog();
802    }
803
804    /**
805     * On text box change.<p>
806     *
807     * @param event the event
808     */
809    @UiHandler("m_textbox")
810    void onTextBoxChange(ValueChangeEvent<String> event) {
811
812        fireChange(false);
813        if (m_infoTimer != null) {
814            m_infoTimer.cancel();
815            m_infoTimer = null;
816        }
817        m_infoTimer = new Timer() {
818
819            @Override
820            public void run() {
821
822                updateResourceInfo(getFormValueAsString());
823                clearInfoTimer();
824            }
825        };
826        m_infoTimer.schedule(300);
827    }
828
829    /**
830     * Toggles the fading element.<p>
831     *
832     * @param faded <code>true</code> to show the fading element.<p>
833     */
834    void setFaded(boolean faded) {
835
836        m_fader.setVisible(faded);
837    }
838
839    /**
840     * Updates the resource info.<p>
841     *
842     * @param path the resource path
843     */
844    void updateResourceInfo(final String path) {
845
846        m_resourceInfoPanel.clear();
847        if (CmsStringUtil.isEmptyOrWhitespaceOnly(path)) {
848            m_resourceInfoPanel.setVisible(false);
849            if (m_hasImage) {
850                removeStyleName(I_CmsLayoutBundle.INSTANCE.galleryFieldCss().hasImage());
851                m_imagePreview.setInnerHTML("");
852            }
853        } else {
854            m_resourceInfoPanel.getElement().getStyle().clearDisplay();
855            if (m_hasImage) {
856                addStyleName(I_CmsLayoutBundle.INSTANCE.galleryFieldCss().hasImage());
857            }
858            CmsRpcAction<CmsResultItemBean> action = new CmsRpcAction<CmsResultItemBean>() {
859
860                @Override
861                public void execute() {
862
863                    getGalleryService().getInfoForResource(path, m_configuration.getLocale(), this);
864                }
865
866                @Override
867                protected void onResponse(CmsResultItemBean result) {
868
869                    displayResourceInfo(result);
870                }
871            };
872            action.execute();
873        }
874        fireResize();
875    }
876
877    /**
878     * Creates the gallery pop-up.<p>
879     *
880     * @return the gallery pop-up
881     */
882    private CmsGalleryPopup createPopup() {
883
884        I_CmsGalleryWidgetHandler handler = new I_CmsGalleryWidgetHandler() {
885
886            @Override
887            public void setWidgetValue(
888                String resourcePath,
889                CmsUUID structureId,
890                CmsCroppingParamBean croppingParameter) {
891
892                setValueFromGallery(resourcePath, structureId, croppingParameter);
893            }
894        };
895        m_configuration.setCurrentElement(getCurrentElement());
896        return new CmsGalleryPopup(handler, m_configuration);
897    }
898
899    /**
900     * Creates custom upload button for galleries which have an upload action configured.
901     *
902     * @return the special upload button
903     */
904    private CmsPushButton createSpecialUploadButton() {
905
906        CmsPushButton uploadButton = new CmsPushButton(I_CmsButton.UPLOAD_SMALL);
907        uploadButton.setText(null);
908        //uploadButton.setTitle(Messages.get().key(Messages.GUI_GALLERY_UPLOAD_TITLE_1, gallery.getPath()));
909        uploadButton.setButtonStyle(ButtonStyle.FONT_ICON, null);
910        uploadButton.addClickHandler(new ClickHandler() {
911
912            @Override
913            public void onClick(ClickEvent event) {
914
915                CmsRpcAction<CmsUUID> action = new CmsRpcAction<CmsUUID>() {
916
917                    @Override
918                    public void execute() {
919
920                        start(0, true);
921                        CmsCoreProvider.getVfsService().getStructureId(m_uploadTarget, this);
922                    }
923
924                    @SuppressWarnings("synthetic-access")
925                    @Override
926                    protected void onResponse(CmsUUID result) {
927
928                        stop(false);
929                        if (result == null) {
930                            return;
931                        }
932                        List<CmsUUID> resultIds = new ArrayList<>();
933                        resultIds.add(result);
934                        Map<String, String> params = new HashMap<>();
935                        params.put("editor", "true");
936                        CmsEmbeddedDialogHandler.openDialog(
937                            m_uploadAction,
938                            resultIds,
939                            params,
940                            id -> updateValueFromId(id));
941                    }
942
943                };
944                action.execute();
945            }
946        });
947        return uploadButton;
948    }
949
950    /**
951     * Initializes the upload drop zone event handlers.<p>
952     *
953     * @param element the drop zone element
954     */
955    private native void initUploadZone(JavaScriptObject element)/*-{
956        // check for file api support
957        if ((typeof FileReader == 'function' || typeof FileReader == 'object')
958                && (typeof FormData == 'function' || typeof FormData == 'object')) {
959            var self = this;
960
961            function isDndEnabled() {
962                return self.@org.opencms.ade.galleries.client.ui.CmsGalleryField::isDndEnabled()();
963            }
964
965            function dragover(event) {
966                event.stopPropagation();
967                event.preventDefault();
968                if (!isDndEnabled()) {
969                    return;
970                }
971                self.@org.opencms.ade.galleries.client.ui.CmsGalleryField::onDragOver()();
972            }
973
974            function dragleave(event) {
975                event.stopPropagation();
976                event.preventDefault();
977                if (!isDndEnabled()) {
978                    return;
979                }
980                self.@org.opencms.ade.galleries.client.ui.CmsGalleryField::onDragOut()();
981            }
982
983            function drop(event) {
984                event.preventDefault();
985                if (!isDndEnabled()) {
986                    return;
987                }
988                self.@org.opencms.ade.galleries.client.ui.CmsGalleryField::onDragOut()();
989                if (self.@org.opencms.ade.galleries.client.ui.CmsGalleryField::m_uploadTarget != null) {
990                    var dt = event.dataTransfer;
991                    var files = dt.files;
992                    self.@org.opencms.ade.galleries.client.ui.CmsGalleryField::openUploadWithFiles(Lcom/google/gwt/core/client/JavaScriptObject;)(files);
993                }
994            }
995
996            element.addEventListener("dragover", dragover, false);
997            element.addEventListener("dragexit", dragleave, false);
998            element.addEventListener("dragleave", dragleave, false);
999            element.addEventListener("dragend", dragleave, false);
1000            element.addEventListener("drop", drop, false);
1001        }
1002    }-*/;
1003
1004    /**
1005     * Opens the upload dialog with the given file references to upload.<p>
1006     *
1007     * @param files the file references
1008     */
1009    private void openUploadWithFiles(JavaScriptObject files) {
1010
1011        JsArray<CmsFileInfo> cmsFiles = files.cast();
1012        List<CmsFileInfo> fileObjects = new ArrayList<CmsFileInfo>();
1013        for (int i = 0; i < cmsFiles.length(); ++i) {
1014            fileObjects.add(cmsFiles.get(i));
1015        }
1016        ((CmsDialogUploadButtonHandler)m_uploadButton.getButtonHandler()).openDialogWithFiles(fileObjects);
1017    }
1018
1019    /**
1020     * Enables the special upload button.
1021     *
1022     * @param specialUpload true if the special upload button should be enabled
1023     */
1024    private void setSpecialUpload(boolean specialUpload) {
1025
1026        m_specialUploadButton.setVisible(specialUpload);
1027        m_uploadButton.setVisible(!specialUpload);
1028
1029    }
1030
1031    /**
1032     * Updates the value to the path for the given structure id.
1033     *
1034     * @param id a structure id
1035     */
1036    private void updateValueFromId(CmsUUID id) {
1037
1038        CmsRpcAction<String> action = new CmsRpcAction<String>() {
1039
1040            @Override
1041            public void execute() {
1042
1043                start(0, false);
1044                CmsCoreProvider.getVfsService().getSitePath(id, this);
1045
1046            }
1047
1048            @Override
1049            protected void onResponse(String result) {
1050
1051                stop(false);
1052                m_textbox.setValue(result, true);
1053
1054            }
1055        };
1056        action.execute();
1057
1058    }
1059}