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.acacia.client.widgets;
029
030import org.opencms.acacia.client.css.I_CmsWidgetsLayoutBundle;
031import org.opencms.acacia.shared.CmsWidgetUtil;
032import org.opencms.gwt.client.CmsCoreProvider;
033import org.opencms.gwt.client.I_CmsHasResizeOnShow;
034import org.opencms.gwt.client.Messages;
035import org.opencms.gwt.client.rpc.CmsRpcAction;
036import org.opencms.gwt.client.ui.CmsPopup;
037import org.opencms.gwt.client.ui.input.CmsCategoryField;
038import org.opencms.gwt.client.ui.input.category.CmsCategoryTree;
039import org.opencms.gwt.shared.CmsCategoryTreeEntry;
040
041import java.util.HashSet;
042import java.util.Iterator;
043import java.util.List;
044import java.util.Map;
045import java.util.Set;
046
047import com.google.gwt.dom.client.Element;
048import com.google.gwt.event.dom.client.ClickEvent;
049import com.google.gwt.event.dom.client.ClickHandler;
050import com.google.gwt.event.dom.client.FocusEvent;
051import com.google.gwt.event.dom.client.FocusHandler;
052import com.google.gwt.event.logical.shared.CloseEvent;
053import com.google.gwt.event.logical.shared.CloseHandler;
054import com.google.gwt.event.logical.shared.ValueChangeEvent;
055import com.google.gwt.event.logical.shared.ValueChangeHandler;
056import com.google.gwt.event.shared.HandlerRegistration;
057import com.google.gwt.user.client.Command;
058import com.google.gwt.user.client.DOM;
059import com.google.gwt.user.client.Event;
060import com.google.gwt.user.client.Event.NativePreviewEvent;
061import com.google.gwt.user.client.Event.NativePreviewHandler;
062import com.google.gwt.user.client.Window;
063import com.google.gwt.user.client.ui.Composite;
064import com.google.gwt.user.client.ui.PopupPanel;
065
066/**
067 * Provides a standard HTML form category widget, for use on a widget dialog.<p>
068 **/
069public class CmsCategoryWidget extends Composite implements I_CmsEditWidget, I_CmsHasResizeOnShow {
070
071    /**
072     * Drag and drop event preview handler.<p>
073     *
074     * To be used while dragging.<p>
075     */
076    protected class CloseEventPreviewHandler implements NativePreviewHandler {
077
078        /**
079         * @see com.google.gwt.user.client.Event.NativePreviewHandler#onPreviewNativeEvent(com.google.gwt.user.client.Event.NativePreviewEvent)
080         */
081        public void onPreviewNativeEvent(NativePreviewEvent event) {
082
083            Event nativeEvent = Event.as(event.getNativeEvent());
084            switch (DOM.eventGetType(nativeEvent)) {
085                case Event.ONMOUSEWHEEL:
086                    int x_coords = nativeEvent.getClientX();
087                    int y_coords = (nativeEvent.getClientY() + Window.getScrollTop());
088
089                    if (((x_coords > (m_xcoordspopup + 605)) || (x_coords < (m_xcoordspopup)))
090                        || ((y_coords > ((m_ycoordspopup + 390))) || (y_coords < ((m_ycoordspopup))))) {
091                        closePopup();
092                    }
093                    break;
094                default:
095                    // do nothing
096            }
097        }
098
099    }
100
101    /** Configuration parameter to set the category to display. */
102    private static final String CONFIGURATION_CATEGORY = "category";
103
104    /** Set the reference url relative to which category repositories are shown. */
105    private static final String CONFIGURATION_REFPATH = "refpath";
106
107    /** Configuration parameter to set the 'selection type' parameter. */
108    private static final String CONFIGURATION_PARENTSELECTION = "parentselection";
109
110    /** Configuration parameter to set the 'selection type' parameter. */
111    private static final String CONFIGURATION_SELECTIONTYPE = "selectiontype";
112
113    /** Configuration parameter to set the collapsing state when opening the selection. */
114    private static final String CONFIGURATION_COLLAPSED = "collapsed";
115
116    /** Configuration parameter to set flag, indicating if categories should be shown separated by repository. */
117    private static final String CONFIGURATION_SHOW_WITH_REPOSITORY = "showwithrepository";
118
119    /** Configuration parameter to set the default height. */
120    private static final int DEFAULT_HEIGHT = 30;
121
122    /** Configuration parameter to set the maximal height. */
123    private static final int MAX_HEIGHT = 242;
124
125    /** Category widget. */
126    protected CmsCategoryField m_categoryField;
127
128    /** The priview handler. */
129    protected HandlerRegistration m_previewHandlerRegistration;
130
131    /** List of all category folder. */
132    protected List<CmsCategoryTreeEntry> m_resultList;
133
134    /** The x-coords of the popup. */
135    protected int m_xcoordspopup;
136
137    /** The y-coords of the popup. */
138    protected int m_ycoordspopup;
139
140    /** The category field. */
141    CmsCategoryTree m_cmsCategoryTree;
142
143    /** The popup panel. */
144    CmsPopup m_cmsPopup;
145
146    /** Height of the display field. */
147    int m_height = DEFAULT_HEIGHT;
148
149    /** Flag indicating the category tree is being loaded. */
150    boolean m_loadingCategoryTree;
151
152    /** List of all selected categories. */
153    Set<String> m_selected;
154
155    /** Map of selected Values in relation to the select level. */
156    String m_selectedValue;
157
158    /** Value of the activation. */
159    private boolean m_active = true;
160
161    /** Single category folder. */
162    private String m_category = "";
163
164    /** List of all possible category folder. */
165    private String m_refPath;
166
167    /** Sets the value if the parent should be selected with the children. */
168    private boolean m_children;
169
170    /** Is true if only one value is set in xml. */
171    private boolean m_isSingleValue;
172
173    /** If true, the category selection opens with collapsed category trees. */
174    private boolean m_collapsed;
175
176    /** If true, the categories are shown separate for each repository. */
177    private boolean m_showWithRepository;
178
179    /**
180     * Constructs an CmsComboWidget with the in XSD schema declared configuration.<p>
181     * @param config The configuration string given from OpenCms XSD
182     */
183    public CmsCategoryWidget(String config) {
184
185        m_categoryField = new CmsCategoryField();
186        m_selected = new HashSet<String>();
187        //parse configuration string and set member variables
188        parseConfiguration(config);
189        m_categoryField.setParentSelection(m_children);
190        m_categoryField.getScrollPanel().addStyleName(I_CmsWidgetsLayoutBundle.INSTANCE.widgetCss().categoryPanel());
191        m_categoryField.addDomHandler(new ClickHandler() {
192
193            public void onClick(ClickEvent event) {
194
195                if ((m_cmsPopup == null) || !m_cmsPopup.isShowing()) {
196                    openPopup();
197                } else {
198                    closePopup();
199                }
200
201            }
202        }, ClickEvent.getType());
203        initWidget(m_categoryField);
204
205    }
206
207    /**
208     * @see com.google.gwt.event.dom.client.HasFocusHandlers#addFocusHandler(com.google.gwt.event.dom.client.FocusHandler)
209     */
210    public HandlerRegistration addFocusHandler(FocusHandler handler) {
211
212        return addDomHandler(handler, FocusEvent.getType());
213    }
214
215    /**
216     * @see com.google.gwt.event.logical.shared.HasValueChangeHandlers#addValueChangeHandler(com.google.gwt.event.logical.shared.ValueChangeHandler)
217     */
218    public HandlerRegistration addValueChangeHandler(ValueChangeHandler<String> handler) {
219
220        return addHandler(handler, ValueChangeEvent.getType());
221    }
222
223    /**
224     * Represents a value change event.<p>
225     */
226    public void fireChangeEvent() {
227
228        ValueChangeEvent.fire(this, getValue());
229
230    }
231
232    /**
233     * @see com.google.gwt.user.client.ui.HasValue#getValue()
234     */
235    public String getValue() {
236
237        String result = "";
238        int y = 0;
239        if (m_isSingleValue) {
240            if (m_selected.size() != 0) {
241                result = m_selected.iterator().next();
242            } else {
243                result = "";
244            }
245        } else {
246            Iterator<String> i = m_categoryField.getAllSitePath().iterator();
247            while (i.hasNext()) {
248                if (y != 0) {
249                    result += ",";
250
251                }
252                result += i.next();
253                y++;
254            }
255        }
256        return result;
257    }
258
259    /**
260     * @see org.opencms.acacia.client.widgets.I_CmsEditWidget#isActive()
261     */
262    public boolean isActive() {
263
264        return m_active;
265    }
266
267    /**
268     * @see org.opencms.acacia.client.widgets.I_CmsEditWidget#onAttachWidget()
269     */
270    public void onAttachWidget() {
271
272        super.onAttach();
273    }
274
275    /**
276     * @see org.opencms.acacia.client.widgets.I_CmsEditWidget#owns(com.google.gwt.dom.client.Element)
277     */
278    public boolean owns(Element element) {
279
280        return getElement().isOrHasChild(element);
281    }
282
283    /**
284     * @see org.opencms.gwt.client.I_CmsHasResizeOnShow#resizeOnShow()
285     */
286    public void resizeOnShow() {
287
288        m_categoryField.resizeOnShow();
289    }
290
291    /**
292     * @see org.opencms.acacia.client.widgets.I_CmsEditWidget#setActive(boolean)
293     */
294    public void setActive(boolean active) {
295
296        if (m_active == active) {
297            return;
298        }
299        m_active = active;
300        // only fire change if the widget was activated
301        if (m_active) {
302            fireChangeEvent();
303        }
304    }
305
306    /**
307     * @see org.opencms.acacia.client.widgets.I_CmsEditWidget#setName(java.lang.String)
308     */
309    public void setName(String name) {
310
311        // no input field so nothing to do.
312    }
313
314    /**
315     * @see com.google.gwt.user.client.ui.HasValue#setValue(java.lang.Object)
316     */
317    public void setValue(String value) {
318
319        setValue(value, false);
320
321    }
322
323    /**
324     * @see com.google.gwt.user.client.ui.HasValue#setValue(java.lang.Object, boolean)
325     */
326    public void setValue(String value, boolean fireEvents) {
327
328        String[] selectedArray = value.split(",");
329        m_selected.clear();
330        for (String selectedCat : selectedArray) {
331            m_selected.add(selectedCat);
332        }
333
334        displayValue();
335
336        if (fireEvents) {
337            fireChangeEvent();
338        }
339    }
340
341    /**
342     * Is called to close the popup and show the new values.<p>
343     */
344    protected void closePopup() {
345
346        List<String> result;
347
348        if (m_previewHandlerRegistration != null) {
349            m_previewHandlerRegistration.removeHandler();
350            m_previewHandlerRegistration = null;
351        }
352        if (m_isSingleValue) {
353            result = m_cmsCategoryTree.getSelected();
354        } else {
355            result = m_cmsCategoryTree.getAllSelectedSitePath();
356        }
357        m_selected.clear();
358        m_selected.addAll(result);
359        displayValue();
360        m_cmsPopup.hide();
361        fireChangeEvent();
362
363    }
364
365    /**
366     * Is called to open the popup.<p>
367     */
368    protected void openPopup() {
369
370        if (m_cmsPopup == null) {
371
372            m_cmsPopup = new CmsPopup(Messages.get().key(Messages.GUI_DIALOG_CATEGORIES_TITLE_0), CmsPopup.WIDE_WIDTH);
373            m_cmsCategoryTree = new CmsCategoryTree(m_selected, 300, m_isSingleValue, m_resultList, m_collapsed);
374            m_cmsPopup.add(m_cmsCategoryTree);
375            m_cmsPopup.setModal(false);
376            m_cmsPopup.setAutoHideEnabled(true);
377            m_cmsPopup.addCloseHandler(new CloseHandler<PopupPanel>() {
378
379                public void onClose(CloseEvent<PopupPanel> event) {
380
381                    closePopup();
382
383                }
384            });
385            m_cmsPopup.addDialogClose(new Command() {
386
387                public void execute() {
388
389                    // do nothing all will done in onClose();
390                }
391            });
392        }
393        if (m_previewHandlerRegistration != null) {
394            m_previewHandlerRegistration.removeHandler();
395        }
396        m_previewHandlerRegistration = Event.addNativePreviewHandler(new CloseEventPreviewHandler());
397        m_cmsCategoryTree.truncate("CATEGORIES", CmsPopup.WIDE_WIDTH - 20);
398        m_cmsPopup.showRelativeTo(m_categoryField);
399        m_xcoordspopup = m_cmsPopup.getPopupLeft();
400        m_ycoordspopup = m_cmsPopup.getPopupTop();
401    }
402
403    /**
404     * Generates the right height for the view.<p>
405     * */
406    protected void setHeight() {
407
408        if (m_categoryField.getValuesSet() > 0) {
409            m_height = (m_categoryField.getValuesSet() * 26) + 4;
410
411            if (m_height > MAX_HEIGHT) {
412                m_height = MAX_HEIGHT;
413                m_categoryField.getScrollPanel().setResizable(true);
414            } else {
415                m_categoryField.getScrollPanel().setResizable(false);
416            }
417        } else {
418            m_height = DEFAULT_HEIGHT;
419
420            m_categoryField.getScrollPanel().setResizable(false);
421        }
422
423        m_categoryField.setHeight(m_height);
424
425    }
426
427    /**
428     * Displays the current value.<p>
429     */
430    private void displayValue() {
431
432        if (m_resultList == null) {
433            if (!m_loadingCategoryTree) {
434                m_loadingCategoryTree = true;
435                // generate a list of all configured categories.
436                final String category = m_category;
437                final String refPath = m_refPath;
438                final boolean showWithRepository = m_showWithRepository;
439                CmsRpcAction<List<CmsCategoryTreeEntry>> action = new CmsRpcAction<List<CmsCategoryTreeEntry>>() {
440
441                    /**
442                     * @see org.opencms.gwt.client.rpc.CmsRpcAction#onResponse(java.lang.Object)
443                     */
444                    @Override
445                    public void execute() {
446
447                        CmsCoreProvider.getService().getCategories(category, true, refPath, showWithRepository, this);
448
449                    }
450
451                    /**
452                     * @see org.opencms.gwt.client.rpc.CmsRpcAction#onResponse(java.lang.Object)
453                     */
454                    @Override
455                    protected void onResponse(List<CmsCategoryTreeEntry> result) {
456
457                        // copy the result to the global variable.
458                        m_resultList = result;
459                        m_loadingCategoryTree = false;
460                        // start to generate the tree view.
461                        m_categoryField.buildCategoryTree(m_resultList, m_selected);
462                        setHeight();
463                    }
464
465                };
466                action.execute();
467            }
468        } else {
469            m_categoryField.buildCategoryTree(m_resultList, m_selected);
470            setHeight();
471        }
472    }
473
474    /**
475     * Help function to parse the configuration.<p>
476     * @param configuration the value to be parsed.
477     *
478     * */
479    private void parseConfiguration(String configuration) {
480
481        Map<String, String> configOptions = CmsWidgetUtil.parsePipeSeparatedConfigString(configuration);
482        if (!configOptions.isEmpty()) {
483            m_category = CmsWidgetUtil.getStringOption(configOptions, CONFIGURATION_CATEGORY, m_category);
484            m_isSingleValue = !("multi".equals(
485                CmsWidgetUtil.getStringOption(configOptions, CONFIGURATION_SELECTIONTYPE, "single")));
486            m_children = CmsWidgetUtil.getBooleanOption(configOptions, CONFIGURATION_PARENTSELECTION);
487            m_collapsed = CmsWidgetUtil.getBooleanOption(configOptions, CONFIGURATION_COLLAPSED);
488            m_showWithRepository = CmsWidgetUtil.getBooleanOption(configOptions, CONFIGURATION_SHOW_WITH_REPOSITORY);
489            m_refPath = CmsWidgetUtil.getStringOption(configOptions, CONFIGURATION_REFPATH, m_refPath);
490        }
491    }
492}