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.preview;
029
030import org.opencms.ade.galleries.client.preview.ui.CmsImagePreviewDialog;
031import org.opencms.ade.galleries.shared.CmsImageInfoBean;
032import org.opencms.gwt.client.CmsCoreProvider;
033import org.opencms.gwt.client.util.I_CmsSimpleCallback;
034
035import java.util.ArrayList;
036import java.util.HashMap;
037import java.util.List;
038import java.util.Map;
039
040import com.google.gwt.event.logical.shared.ValueChangeEvent;
041import com.google.gwt.event.logical.shared.ValueChangeHandler;
042
043/**
044 * Image preview dialog controller handler.<p>
045 *
046 * Delegates the actions of the preview controller to the preview dialog.
047 *
048 * @since 8.0.0
049 */
050public class CmsImagePreviewHandler extends A_CmsPreviewHandler<CmsImageInfoBean>
051implements ValueChangeHandler<CmsCroppingParamBean> {
052
053    /** Enumeration of image tag attribute names. */
054    public enum Attribute {
055        /** Image align attribute. */
056        align,
057        /** Image alt attribute. */
058        alt,
059        /** Image class attribute. */
060        clazz,
061        /** Image copyright info. */
062        copyright,
063        /** Image direction attribute. */
064        dir,
065        /** No image selected if this attribute is present. */
066        emptySelection,
067        /** The image hash. */
068        hash,
069        /** Image height attribute. */
070        height,
071        /** Image hspace attribute. */
072        hspace,
073        /** Image id attribute. */
074        id,
075        /** Image copyright flag. */
076        insertCopyright,
077        /** Image link original flag. */
078        insertLinkOrig,
079        /** Image spacing flag. */
080        insertSpacing,
081        /** Image subtitle flag. */
082        insertSubtitle,
083        /** Image language attribute. */
084        lang,
085        /** Image link path. */
086        linkPath,
087        /** Image link target. */
088        linkTarget,
089        /** Image longDesc attribute. */
090        longDesc,
091        /** Image style attribute. */
092        style,
093        /** Image title attribute. */
094        title,
095        /** Image vspace attribute. */
096        vspace,
097        /** Image width attribute. */
098        width
099    }
100
101    /** List of handlers for cropping changes. */
102    private List<Runnable> m_croppingHandlers = new ArrayList<>();
103
104    /** The cropping parameter. */
105    private CmsCroppingParamBean m_croppingParam;
106
107    /** The image format handler. */
108    private CmsImageFormatHandler m_formatHandler;
109
110    /** List of handlers for focal point changes. */
111    private List<Runnable> m_imagePointHandlers = new ArrayList<>();
112
113    /** The focal point controller. */
114    private CmsFocalPointController m_pointController;
115
116    /** The preview dialog. */
117    private CmsImagePreviewDialog m_previewDialog;
118
119    /** The image container width. */
120    private int m_containerWidth;
121
122    /** The image container height. */
123    private int m_containerHeight;
124
125    /**
126     * Constructor.<p>
127     *
128     * @param resourcePreview the resource preview instance
129     */
130    public CmsImagePreviewHandler(CmsImageResourcePreview resourcePreview) {
131
132        super(resourcePreview);
133        m_previewDialog = resourcePreview.getPreviewDialog();
134        m_pointController = new CmsFocalPointController(
135            () -> m_croppingParam,
136            this::getImageInfo,
137            this::onImagePointChanged);
138    }
139
140    /**
141     * Adds a handler for cropping changes.<p>
142     *
143     * @param action the handler to add
144     */
145    public void addCroppingChangeHandler(Runnable action) {
146
147        m_croppingHandlers.add(action);
148    }
149
150    /**
151     * Adds a handler for focal point changes.<p>
152     *
153     * @param onImagePointChanged the handler to add
154     */
155    public void addImagePointChangeHandler(Runnable onImagePointChanged) {
156
157        m_imagePointHandlers.add(onImagePointChanged);
158    }
159
160    /**
161     * Returns the image cropping parameter bean.<p>
162     *
163     * @return the image cropping parameter bean
164     */
165    public CmsCroppingParamBean getCroppingParam() {
166
167        return m_croppingParam;
168    }
169
170    /**
171     * Gets the focal point controller.<p>
172     *
173     * @return the focal point controller
174     */
175    public CmsFocalPointController getFocalPointController() {
176
177        return m_pointController;
178    }
179
180    /**
181     * Gets the format handler.<p>
182     *
183     * @return the format handler
184     */
185    public CmsImageFormatHandler getFormatHandler() {
186
187        return m_formatHandler;
188
189    }
190
191    /**
192     * Returns the name of the currently selected image format.<p>
193     *
194     * @return the format name
195     */
196    public String getFormatName() {
197
198        String result = "";
199        if ((m_formatHandler != null) && (m_formatHandler.getCurrentFormat() != null)) {
200            result = m_formatHandler.getCurrentFormat().getName();
201        }
202        return result;
203    }
204
205    /**
206     * Returns image tag attributes to set for editor plugins.<p>
207     *
208     * @param callback the callback to execute
209     */
210    public void getImageAttributes(I_CmsSimpleCallback<Map<String, String>> callback) {
211
212        Map<String, String> result = new HashMap<String, String>();
213        result.put(Attribute.hash.name(), String.valueOf(getImageIdHash()));
214        m_formatHandler.getImageAttributes(result);
215        m_previewDialog.getImageAttributes(result, callback);
216    }
217
218    /**
219     * Returns the structure id hash of the previewed image.<p>
220     *
221     * @return the structure id hash
222     */
223    public int getImageIdHash() {
224
225        return m_resourceInfo.getHash();
226    }
227
228    /**
229     * Gets the image information.<p>
230     *
231     * @return the image information
232     */
233    public CmsImageInfoBean getImageInfo() {
234
235        return m_resourceInfo;
236    }
237
238    /**
239     * Returns the cropping parameter.<p>
240     *
241     * @param imageHeight the original image height
242     * @param imageWidth the original image width
243     *
244     * @return the cropping parameter
245     */
246    public String getPreviewScaleParam(int imageHeight, int imageWidth) {
247
248        int maxHeight = m_containerHeight;
249        int maxWidth = m_containerWidth;
250
251        if ((m_croppingParam != null) && (m_croppingParam.isCropped() || m_croppingParam.isScaled())) {
252            // NOTE: getREstrictedSizeScaleParam does not work correctly if there isn't actually any cropping/scaling, so we explicitly don't use it in this case
253            return m_croppingParam.getRestrictedSizeScaleParam(maxHeight, maxWidth);
254        }
255        if ((imageHeight <= maxHeight) && (imageWidth <= maxWidth)) {
256            return ""; // dummy parameter, doesn't actually do anything
257        }
258        CmsCroppingParamBean restricted = new CmsCroppingParamBean();
259
260        boolean tooHigh = imageHeight > maxHeight;
261        boolean tooWide = imageWidth > maxWidth;
262        double shrinkX = (1.0 * imageWidth) / maxWidth;
263        double shrinkY = (1.0 * imageHeight) / maxHeight;
264        double aspectRatio = (1.0 * imageWidth) / imageHeight;
265        if (tooHigh && tooWide) {
266            if (shrinkX > shrinkY) {
267                restricted.setTargetWidth(maxWidth);
268                restricted.setTargetHeight((int)(maxWidth / aspectRatio));
269            } else {
270                restricted.setTargetHeight(maxHeight);
271                restricted.setTargetWidth((int)(maxHeight * aspectRatio));
272            }
273        } else if (tooWide) {
274            restricted.setTargetWidth(maxWidth);
275            restricted.setTargetHeight((int)(maxWidth / aspectRatio));
276        } else if (tooHigh) {
277            restricted.setTargetHeight(maxHeight);
278            restricted.setTargetWidth((int)(maxHeight * aspectRatio));
279        } else {
280            restricted.setTargetWidth(imageWidth);
281            restricted.setTargetHeight(imageHeight);
282        }
283        return restricted.toString();
284    }
285
286    /**
287     * @see com.google.gwt.event.logical.shared.ValueChangeHandler#onValueChange(com.google.gwt.event.logical.shared.ValueChangeEvent)
288     */
289    public void onValueChange(ValueChangeEvent<CmsCroppingParamBean> event) {
290
291        m_croppingParam = event.getValue();
292        String viewLink = m_resourcePreview.getViewLink();
293        if (viewLink == null) {
294            viewLink = CmsCoreProvider.get().link(m_resourcePreview.getResourcePath());
295        }
296        m_previewDialog.resetPreviewImage(
297            viewLink + "?" + getPreviewScaleParam(m_croppingParam.getOrgHeight(), m_croppingParam.getOrgWidth()));
298        onCroppingChanged();
299    }
300
301    /**
302     * Sets the image format handler.<p>
303     *
304     * @param formatHandler the format handler
305     */
306    public void setFormatHandler(CmsImageFormatHandler formatHandler) {
307
308        m_formatHandler = formatHandler;
309        m_croppingParam = m_formatHandler.getCroppingParam();
310        m_formatHandler.addValueChangeHandler(this);
311        onCroppingChanged();
312    }
313
314    /**
315     *
316     * Sets the dimensions of the area the image is going to be placed in.
317     *
318     * @param offsetWidth the container width
319     * @param offsetHeight the container height
320     */
321    public void setImageContainerSize(int offsetWidth, int offsetHeight) {
322
323        m_containerWidth = offsetWidth;
324        m_containerHeight = offsetHeight;
325    }
326
327    /**
328     * Calls all cropping change handlers.
329     */
330    private void onCroppingChanged() {
331
332        for (Runnable action : m_croppingHandlers) {
333            action.run();
334        }
335    }
336
337    /**
338     * Calls all focal point change handlers.<p>
339     */
340    private void onImagePointChanged() {
341
342        for (Runnable handler : m_imagePointHandlers) {
343            handler.run();
344        }
345
346    }
347
348}