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