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